OCaml函数传递一个较少的参数

时间:2016-01-22 02:09:53

标签: f# pattern-matching ocaml grammar ml

我正在寻找家庭作业的解决方案,并且代码实现了一个OCaml函数,该函数接受两个参数但是当它被调用时,它只传递一个参数。

let rec func2 r x = match r with
  | [] -> []
  | (nt,rhs)::t -> if nt = x then rhs::(func2 t x) else func2 t x;;

let func1 r = fun x -> func2 r x;;

我会通过调用(func1 awksub_rules)来调用类似下面的语法规则的func1

let awksub_rules = [
  Expr, [T"("; N Expr; T")"];
  Expr, [N Num];
  Num, [T"0"];
  Num, [T"1"]
]

Expr和Num只是已定义的非终结类型,T符号表示终端类型。

我感到困惑,因为func1只接受awksub_rules作为参数,但函数声明有两个函数。

预期输出为

function 
  | Expr -> [[T"("; N Expr; T")"]; [N Num]]
  | Num -> [[T"0"]; [T"1"]]

我可以看到func1正确返回一个函数,并且func2处理检查左侧(Expr或Num)是否相同,以便它可以连接到列表。但是我不知道传递给x的是什么。

2 个答案:

答案 0 :(得分:6)

当使用一个参数调用func1时,它会返回另一个函数,让我们将其称为func3

let func3 = func1 awksub_rules

此时,还没有参数x。这个新函数仍然期望传递这个参数。

当你调用这个新函数时,你将传入x的值,计算将开始:

let result = func3 Num

我还想指出func1func2在逻辑上是等价的,因为ML中的机制称为" partial application"。也就是说,您可以在func2的任何地方使用func1,效果相同:

let func3 = func2 awksub_rules
let result = func3 Num

答案 1 :(得分:4)

Fyodor Soikin的回答解释了为什么func1func2在逻辑上是相同的。但是,我不希望你离开这种想法,认为有一些神奇的语言特征称为“部分应用”。为了扩展你的想法,你需要了解这是如何从OCaml中的函数如何工作中产生的。

在ML语言中,没有“接受两个参数的函数”这样的东西。 ML中的每个函数只需要一个参数(不是零,不是两个,不是三个;总是一个)。 OCaml语法

let rec func2 r x = ...

的语法糖
let rec func2 = function r -> function x -> ...

即。它只是一个返回另一个函数的函数的定义。这就是为什么函数的类型将类似于a -> b -> c->是右关联的,因此它与a -> (b -> c)相同) - 它表示它是一个函数a,并返回b -> c类型的函数。

当你去应用这个函数时,你认为传递两个参数,如func2 foo bar,实际上是(因为函数应用程序是左关联的)(func2 foo) bar,即它首先应用表达式func2 foo,然后将函数的结果应用于表达式bar

通过接受一个参数并返回带有另一个参数的函数等来接受“多个参数”的做法被称为“currying”。 OCaml和其他ML语言为定义curried函数提供了方便的语法(如上所述,OCaml中的通用语法let允许您定义curried函数,只是将参数列在彼此旁边),而在其他语言中编写curried函数需要更详细的语法。这很方便,大多数时候,我们忘记它并将其视为多个参数。但要记住它的真正意义总是很重要。

(请注意,OCaml编译器可能会或可能不会将curry函数优化为带有多个参数的机器代码函数,但这是一个您不应该在语言级别关注的实现细节。)