我试图理解用于生成从列表的N个元素中选择的K个不同对象的组合的解决方案。以下是代码:
let extract k list =
let rec aux k acc emit = function
| [] -> acc
| h :: t ->
if k = 1 then aux k (emit [h] acc) emit t else
let new_emit x = emit (h :: x) in
aux k (aux (k-1) acc new_emit t) emit t
in
let emit x acc = x :: acc in
aux k [] emit list;;
emit函数定义为接受两个参数:
let emit x acc = x :: acc
所以我不太明白以下行是如何工作的,因为它调用emit只给出一个参数:
let new_emit x = emit (h :: x)
此外,new_emit函数只接受一个参数并作为参数传递给aux函数,它如何处理以下行(这里的emit通过给出两个参数来调用):
if k = 1 then aux k (emit [h] acc) emit t
答案 0 :(得分:1)
OCaml中的函数通常是curry,这意味着通过获取一个参数并返回一个接受下一个参数的函数来表示多个参数函数(依此类推)。 OCaml有一些语法糖可以使这个更好看:let f x y = ...
是let f = fun x -> fun y -> ...
的缩写。
通常程序员通过一次传递所有参数来使用这些函数,但是可以只传递一个并且返回部分应用的'因此起作用。这就是emit
发生的事情。
因此,您可以阅读let emit_new x = emit (h :: x)
来定义emit
的版本,其中第一个参数已经提供。
答案 1 :(得分:0)
你在这里缺少的一点是,由于currying和一流的功能,函数的参数数量并不像你想象的那么严格。
在这种特殊情况下,emit
的定义为
let emit x acc = x :: acc
为其指定'a -> 'a list -> 'a list
类型。此类型可以有两个不同的读数,您可以将其视为一个带有两个参数的函数,一个类型为'a
,另一个类型为'a list
,并返回类型为'a list
的对象。但是,您也可以将其作为函数读取,该函数采用类型为'a
的一个参数,并返回类型为'a list -> 'a list
的函数。
定义
let new_emit x = emit (h :: x)
使用这种咖喱解释:因为emit
具有类型
'a -> 'a list -> 'a list
,将其应用于h::x
会生成类型函数
'a list -> 'a list
因此函数new_emit
具有类型
'a -> 'a list -> 'a list
。换句话说,函数new_emit
仍然接受两个输入参数,即使它定义只涉及一个参数。注意,为了使事情更容易理解,new_emit
的定义也可以写成
let new_emit x = fun acc -> emit (h :: x) acc
或
let new_emit x acc = emit (h :: x) acc
在上下文中,emit element_list combination_list
用于将新组合添加到组合列表中,方法是取element_list
并添加所有先前选取的元素。然后使用new_emit
的定义来挑选新元素h
。换句话说,这一行
if k = 1 then aux k (emit [h] acc) emit t else
表示将元素列表[h]
加上所有先前拾取的元素添加到组合列表中,因为所有元素都已被选中,而
let new_emit x = emit (h :: x) in
aux k (aux (k-1) acc new_emit t) emit t
可以分解为:
首先选择元素h
:
let new_emit x = emit (h :: x)
然后构建h
存在的所有组合:
let combination_where_h_was_selected = aux (k-1) acc new_emit t
然后构建h不存在的所有组合:
aux k combination_where_h_was_selected emit t
P.S .: 作为关于函数参数数量主题的更高级的评论,请注意甚至“可变”函数完全可能在OCaml中。例如,定义列表的深奥而低效的方法是
let start f = f (fun x -> x)
let elt x t k = k (fun l -> t (fun l -> x ::l) l)
let stop f = f (fun x -> x) []
let [] = start stop
let l = start (elt 1) (elt 2) (elt 3) (elt 4) stop
;; assert ( l = [1;2;3;4] )