Я определил структуру дерева выражение в F # следующим образом:Неполное совпадение с и модели
type Num = int
type Name = string
type Expr =
| Con of Num
| Var of Name
| Add of Expr * Expr
| Sub of Expr * Expr
| Mult of Expr * Expr
| Div of Expr * Expr
| Pow of Expr * Expr
| Neg of Expr
Я хотел, чтобы иметь возможность довольно-печать выражение дерева, так что я сделал следующее:
let (|Unary|Binary|Terminal|) expr =
match expr with
| Add(x, y) -> Binary(x, y)
| Sub(x, y) -> Binary(x, y)
| Mult(x, y) -> Binary(x, y)
| Div(x, y) -> Binary(x, y)
| Pow(x, y) -> Binary(x, y)
| Neg(x) -> Unary(x)
| Con(x) -> Terminal(box x)
| Var(x) -> Terminal(box x)
let operator expr =
match expr with
| Add(_) -> "+"
| Sub(_) | Neg(_) -> "-"
| Mult(_) -> "*"
| Div(_) -> "/"
| Pow(_) -> "**"
| _ -> failwith "There is no operator for the given expression."
let rec format expr =
match expr with
| Unary(x) -> sprintf "%s(%s)" (operator expr) (format x)
| Binary(x, y) -> sprintf "(%s %s %s)" (format x) (operator expr) (format y)
| Terminal(x) -> string x
Тем не менее, мне не очень нравится подход failwith
для функции operator
, так как он не безопасен во время компиляции. Так что я переписал его в качестве активного шаблона:
let (|Operator|_|) expr =
match expr with
| Add(_) -> Some "+"
| Sub(_) | Neg(_) -> Some "-"
| Mult(_) -> Some "*"
| Div(_) -> Some "/"
| Pow(_) -> Some "**"
| _ -> None
Теперь я могу переписать мою format
функции красиво выглядит следующим образом:
let rec format expr =
match expr with
| Unary(x) & Operator(op) -> sprintf "%s(%s)" op (format x)
| Binary(x, y) & Operator(op) -> sprintf "(%s %s %s)" (format x) op (format y)
| Terminal(x) -> string x
Я предполагал, так как F # магии, что это будет просто работать. К сожалению, компилятор тогда предупреждает меня о неполных совпадениях шаблонов, потому что он не может видеть, что все, что соответствует Unary(x)
, также будет соответствовать Operator(op)
, и все, что соответствует Binary(x, y)
, также будет соответствовать Operator(op)
. И я считаю, что подобные предупреждения так же плохи, как ошибки компилятора.
Итак, мои вопросы: есть ли конкретная причина, по которой это не работает (например, я где-то оставил магическую аннотацию или есть что-то, чего я просто не вижу)? Есть ли простой обходной путь, который я мог бы использовать, чтобы получить тип безопасности, который я хочу? И есть ли неотъемлемая проблема с этим типом проверки времени компиляции, или это то, что F # может добавить в какой-то будущий выпуск?
Я думаю, что такая проблема вряд ли будет исправлена. В общем случае это потребует решения проблемы остановки. Я думаю, что самым изящным решением было бы добавить дополнительный слой шаблонов, чтобы вы вернули «Unary (x, op)». –
Я действительно рассматривал это, но я хотел сохранить свой шаблон конкретным для одного случая использования (классифицируя arity выражения и извлекая его аргументы). – luksan