我如下定義在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
但是,我不太喜歡operator
函數的failwith
方法,因爲它不是編譯時安全的。所以我重寫了它作爲有效模式:
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