Упрощение сопоставления вложенных шаблонов F#

Я пишу простой синтаксический анализатор выражений на F#, и для каждого оператора я хочу поддерживать только определенное количество операндов (например, два по модулю, три для If). Вот что у меня есть:

type Operator =
    | Modulo
    | Equals
    | If

let processOperator operands operator =
    match operator with
    | Modulo ->
        match operands with
        | [ a:string; b:string ] -> (Convert.ToInt32(a) % Convert.ToInt32(b)).ToString()
        | _ -> failwith "wrong number of operands"
    | Equals ->
        match operands with
        | [ a; b ] -> (a = b).ToString()
        | _ -> failwith "wrong operands"
    | If ->
        match operands with 
        | [ a; b; c ] -> (if Convert.ToBoolean(a) then b else c).ToString()
        | _ -> failwith "wrong operands"

Я хотел бы избавиться или упростить совпадения внутреннего списка. Каков наилучший способ добиться этого? Должен ли я использовать несколько охранников?


person Maciej Wozniak    schedule 16.03.2014    source источник


Ответы (2)


Сложите соответствующие операнды:

let processOperator operands operator =
    match operator, operands with
    | Modulo, [a; b] -> (Convert.ToInt32(a) % Convert.ToInt32(b)).ToString()
    | Equals, [a; b] -> (a = b).ToString()
    | If, [ a; b; c ] -> (if Convert.ToBoolean(a) then b else c).ToString()
    | _ -> failwith "wrong number of operands"

А еще лучше, если можете, измените тип данных на следующий.

type Operator =
    | Modulo of string * string
    | Equals of string * string
    | If of string * string * string

Тогда в матче вы уже не сможете потерпеть неудачу.

person Søren Debois    schedule 16.03.2014
comment
Разве это не приводит к той же проблеме в другой части кода? - person Nate C-K; 16.03.2014
comment
Вы имеете в виду изменение типа данных. да. Но (Густаво и) я бы сказал, что это место там, наряду со всеми преобразованиями из целых чисел и логических значений - см. Хороший ответ Густаво ниже. - person Søren Debois; 16.03.2014
comment
Забавно, мы оба одновременно ответили почти на одно и то же решение и на одну и ту же рекомендацию, это запах дизайна (хороший) ;) - person Gus; 16.03.2014
comment
@Gustavo & Søren: Спасибо, я рассматривал ваше первое решение, но, поскольку я могу менять типы данных, второе кажется лучше и чище. Я не могу отметить оба ответа, поэтому голосование идет за вас обоих и за ответ Серену (первый). - person Maciej Wozniak; 16.03.2014

open System

type Operator =
    | Modulo
    | Equals
    | If

let processOperator operands operator =
    match (operator, operands) with
    | Modulo, [a: string; b] -> string ((int a) % (int b))
    | Equals, [a; b] -> string (a = b)
    | If, [a; b; c]  -> if Convert.ToBoolean(a) then b else c
    | _ -> failwith "wrong number of operands"

Но я бы предложил перенести эту логику операндов в синтаксический анализатор, таким образом вы получите чистое операторное выражение, которое более идиоматично и прямолинейно для обработки, в конце у вас будет что-то вроде этого:

open System

type Operator =
    | Modulo of int * int
    | Equals of int * int
    | If of bool * string * string

let processOperator = function
    | Modulo (a, b) -> string (a % b)
    | Equals (a, b) -> string (a = b)
    | If (a, b, c)  -> if a then b else c
person Gus    schedule 16.03.2014