我对在一个代码中何时使用或实现仿函数感到有些困惑。我在下面包含了一些代码,其中有两个函数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新秀,所以请在你的回复中考虑一下。感谢信。
答案 0 :(得分:4)
由于没有模块,Functors并不真正适用于此,但您可以使用更高阶函数直接攻击它。
关键是要观察每个构造函数需要一个变换,因此需要一个函数参数。此模式在某些方面类似于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)
这是否比原始代码更可取是一种品味问题。