我对何时在代码中使用或实现仿函数有点困惑。我在下面包含了一些代码,它有两个函数 display_expr、cal_expr,这两个函数共享相同的形式,但实现方式不同。这是一个我会考虑创建一个代表两个函数核心功能的函数的地方吗?
type expr =
| Add of expr * expr
| Minus of expr * expr
| Multi of expr * expr
| Divide of expr * expr
| Value of int;;
let rec display_expr e =
match e with
| Add (a1, a2) -> "(" ^ display_expr a1 ^ " + " ^ display_expr a2 ^ ")"
| Minus (m1, m2) -> "(" ^ display_expr m1 ^ " - " ^ display_expr m2 ^ ")"
| Multi (m1, m2) -> "(" ^ display_expr m1 ^ " * " ^ display_expr m2 ^ ")"
| Divide (d1, d2) -> "(" ^ display_expr d1 ^ " / " ^ display_expr d2 ^ ")"
| Value v -> string_of_int v;;
let rec cal_expr e =
match e with
| Add (a1, a2) -> (cal_expr a1) + (cal_expr a2)
| Minus (m1, m2) -> (cal_expr m1) - (cal_expr m2)
| Multi (m1, m2) -> (cal_expr m1) * (cal_expr m2)
| Divide (d1, d2) -> (cal_expr d1) / (cal_expr d2)
| Value v -> v;;
let equ =
Multi(Value 34,
Add(Value 24,
Divide(Value 24,
Minus(Value 10, Value 7)
)
)
);;
Printf.fprintf stdout "%d = %s\n" (cal_expr equ) (display_expr equ);;
注意:我尝试为上面的代码编写一个仿函数解决方案,一旦我发现仿函数需要 display_expr 和 cal_expr 返回的值的通用或组合类型,我就开始工作了。
另外:我是一个极端的 OCaml 菜鸟,所以请在您的回复中考虑这一点。谢谢。
最佳答案
由于没有模块,仿函数在这里并不真正适用,但是您可以使用高阶函数直接攻击它。
关键是要观察到每个构造函数都需要一个转换,因此需要一个函数参数。这种模式在某些方面类似于 List.fold_right
,可以认为是替换列表构造函数的操作。
type expr =
| Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
| Int of int
let transform ~add ~sub ~mul ~div ~int expr =
let rec tx = function
| Add (x, y) -> add (tx x) (tx y)
| Sub (x, y) -> sub (tx x) (tx y)
| Mul (x, y) -> mul (tx x) (tx y)
| Div (x, y) -> div (tx x) (tx y)
| Int x -> int x in
tx expr
let binary_op_str sep a b = "(" ^ a ^ sep ^ b ^ ")"
let display_expr = transform
~add:(binary_op_str " + ")
~sub:(binary_op_str " - ")
~mul:(binary_op_str " * ")
~div:(binary_op_str " / ")
~int:string_of_int
let cal_expr = transform
~add:(+)
~sub:(-)
~mul:( * )
~div:(/)
~int:(fun x -> x)
这是否比您的原始代码更可取是一个品味问题。
关于OCaml - 何时使用仿函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21224539/