Mam zdefiniowane struktury drzewa wyrażenie w F # następująco:Niekompletne mecz z I wzory
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
Chciałem móc całkiem-print drzewo wyrażenie tak zrobiłem następujące:
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
Jednak nie podoba mi się podejście failwith
dla funkcji operator
, ponieważ nie jest to bezpieczne w czasie kompilacji. Więc przepisał go jako aktywnego wzoru:
let (|Operator|_|) expr =
match expr with
| Add(_) -> Some "+"
| Sub(_) | Neg(_) -> Some "-"
| Mult(_) -> Some "*"
| Div(_) -> Some "/"
| Pow(_) -> Some "**"
| _ -> None
Teraz mogę przepisać mój format
funkcję pięknie następująco:
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
Przypuszczałem, ponieważ F # jest magia, że to po prostu działa. Niestety, kompilator ostrzega mnie przed niekompletnymi dopasowaniami do wzorca, ponieważ nie widzi, że cokolwiek pasującego do Unary(x)
będzie również pasowało do Operator(op)
, a wszystko, co pasuje do Binary(x, y)
, będzie również pasować do Operator(op)
. I uważam, że takie ostrzeżenia są tak złe, jak błędy kompilatora.
Moje pytania brzmią: czy istnieje konkretny powód, dla którego to nie działa (jak zostawiłem gdzieś magiczną adnotację, czy jest coś, czego po prostu nie widzę)? Czy istnieje prosty sposób obejścia tego problemu, aby uzyskać pożądany rodzaj bezpieczeństwa? I czy jest jakiś nieodłączny problem z tym typem sprawdzania podczas kompilacji, czy jest to coś, co F # może dodać w przyszłym wydaniu?
Myślę, że ten rodzaj problemu raczej nie zostanie naprawiony. W ogólnym przypadku będzie to wymagało rozwiązania problemu zatrzymania. Myślę, że najbardziej eleganckim rozwiązaniem byłoby dodanie dodatkowej warstwy wzorców, abyś zwrócił 'Unary (x, op)'. –
Właściwie to rozważałem, ale chciałem, aby mój wzorzec był specyficzny dla jednego przypadku użycia (klasyfikując arność wyrażenia i wyciągając jego argumenty). – luksan