2016-07-01 1 views
2

Мне нужно создать калькулятор в F #, и я застрял на задаче.F # Как анализировать и оценивать выражения калькулятора со строковым вводом

Мне нужно передать сумму в виде строки в консоль, например. «4 + 5» и проанализировать и рассчитать.

любые идеи?

любая помощь будет принята с благодарностью

open System 

let rec calculator() = 
    printf "Choose a sum type \n1: Addition\n2: Subtraction\n3: Mulitiplication\n4: Division\n\n\n" 

    let input = Console.ReadLine(); 

    printf "now type in 2 numbers\n" 

    let num1 = Console.ReadLine(); 
    let num2 = Console.ReadLine(); 

    let a : int = int32 num1 
    let b : int = int32 num2 

    let addition x y = x + y 
    let subtraction x y = x - y 
    let multiply x y = x * y 
    let divide x y = x/y 

    match input with 

    | "1" -> printfn("The result is: %d")(addition a b) 
    | "2" -> printfn("The result is: %d")(subtraction a b) 
    | "3" -> printfn("The result is: %d")(multiply a b) 
    | "4" -> printfn("The result is: %d")(divide a b) 

    ignore(Console.ReadKey()) 
    ignore(calculator()) 

calculator() 
+0

См. [Int32.Parse Method (String)] (https://msdn.microsoft.com/en-us/library/b3h1hf19 (v = vs.110) .aspx) –

+1

Интерес: [Калькулятор формул] (http://www.fssnip.net/4Y) –

+0

Я взглянул на метод Int32.Parse, но борюсь с тем, как читать в операторе сумму –

ответ

3

PARSING выражения, как 4 + 5 в прочном, как правило, включает в себя, опираясь на хорошие инструменты, как FsLex/FsYacc или FParsec.

Опытные разработчики, которые любят делать вещи с нуля (не всегда хорошо), могут реализовать что-то, называемое Recursive-Decent-Parser.

Другие считают, что для их разбора требуется ответ RegEx. RegEx, однако, ограничен тем, что вы можете достичь с ним. Например, как вы определяете RegEx, который правильно анализирует следующее выражение: 3 + 3*(x+3)/2. Кроме того RegEx код, как правило, неясными и трудно расшифровать, рассмотреть этот общий фрагмент кода для проверки электронной почты:

^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-][email protected][a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$ 

В дополнение к сложным это также неполным. Более полные можно найти здесь: Using a regular expression to validate an email address.

Кроме того, RegEx не генерирует сообщений об ошибках, чтобы сообщить пользователю, почему выражение не удалось проанализировать.

Другой распространенный (неполный & неэффективный) подход анализа, используемый многими, использует String.Split, чтобы разделить строку на операторы.

Например:

let r = 
    "1+2+3".Split '+'    // Produces an array: [|"1", "2", "3"|] 
    |> Seq.map int    // Maps to seq of ints 
    |> Seq.reduce (+)    // Reduces the seq using (+) 

Взятые на это логический вывод, вы можете в конечном итоге с анализатором, который выглядит, как этот

// Very inefficient, can't handle sub expressions, no error reporting... 
// Please use this as an illustration only, not production code 
let stupidParse = 
    let split (c: char) (s: string) = s.Split c 
    let trim (s: string) = s.Trim() 
    let op c r f = split c >> Seq.map (trim >> f) >> Seq.reduce r 
    int |> op '/' (/) |> op '*' (*) |> op '-' (-) |> op '+' (+) 

[<EntryPoint>] 
let main argv = 
    let examples = [| "1"; "1-3"; "1*3"; "1 + 2*3 - 2" |] 
    for example in examples do 
    printfn "%s -> %d" example <| stupidParse example 
    0 

Но, как говорится в комментарии, я никогда не хотел бы парсер как это ввести код производства. Используйте соответствующие инструменты, например FsLex/FsYacc или FParsec.

3

Мне нужно передать сумму в виде строки в консоль, например. «4 + 5» и проанализировать и рассчитать.

Если вы уверены, что ваша строка представляет собой последовательность чисел, разделённых '+' и, возможно, белые пространства, вы можете сделать что-то вроде этого:

"4 + 5".Split '+' |> Seq.sumBy (int) 

Что делать? .Split '+' отделяет строку символом + и создает последовательность строк. В этом примере последовательность будет выглядеть как [|"4 "; " 5"|]. Функция Seq.sumBy применяет заданную функцию к каждому элементу последовательности и суммирует результат. Мы используем функцию (int) для преобразования строк в числа.

Учтите, что это решение не провал, если строка содержит либо символ, отличный +, пробелов и цифр или если строка без знака отделяется + (например + 7 + 8 или 7 ++ 8).

Возможно, вы захотите поймать System.FormatException. Вы бы в конечном итоге с чем-то вроде

let sumString (input:string) : int = 
    try 
     input.Split '+' |> Seq.sumBy (int) 
    with 
    | :? System.FormatException -> 
     print "This does not look like a sum. Let's just assume the result is zero." 
     0 

Это будет только выход 0 для любой неверной формулы. Еще один вариант, чтобы избежать исключения, выбрасывает все нежелательные символы и пустые строки:

let sumString (input:System.String) : int = 
    (input |> String.filter (fun c -> ['0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'; '+'] |> List.contains c)).Split '+' 
    |> Seq.filter (((<) 0) << String.length) 
    |> Seq.sumBy (int) 

Что делает этот код? String.filter запрашивает нашу анонимную функцию для каждого символа, следует ли ее учитывать. Наша анонимная функция проверяет, находится ли символ в списке допустимых символов. Результатом является новая строка, содержащая только цифры и +. Мы разделили эту строку на +.

Прежде чем передать наш список строк в Seq.sumBy (int), мы фильтруем наши пустые строки. Это делается с помощью Seq.filter и состав функций: (<) возвращает true, если первый параметр меньше второго. Мы используем currying для получения (<) 0, который проверяет, является ли данное целое число больше 0. Мы составляем эту функцию с String.length, которая отображает строку в целое число, указывающее ее длину.

После того, как Seq.filter сыграет с этой функцией, мы передаем полученный список Seq.sumBy (int), как указано выше.

Это, однако, может привести к неожиданным результатам для чего угодно, кроме сумм. "4 * 5 + 7" даст 52.