我正在寻找家庭作业的解决方案,并且代码实现了一个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的是什么。
答案 0 :(得分:6)
当使用一个参数调用func1
时,它会返回另一个函数,让我们将其称为func3
:
let func3 = func1 awksub_rules
此时,还没有参数x
。这个新函数仍然期望传递这个参数。
当你调用这个新函数时,你将传入x
的值,计算将开始:
let result = func3 Num
我还想指出func1
和func2
在逻辑上是等价的,因为ML中的机制称为" partial application"。也就是说,您可以在func2
的任何地方使用func1
,效果相同:
let func3 = func2 awksub_rules
let result = func3 Num
答案 1 :(得分:4)
Fyodor Soikin的回答解释了为什么func1
和func2
在逻辑上是相同的。但是,我不希望你离开这种想法,认为有一些神奇的语言特征称为“部分应用”。为了扩展你的想法,你需要了解这是如何从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函数优化为带有多个参数的机器代码函数,但这是一个您不应该在语言级别关注的实现细节。)