Ocaml 99问题:无法理解生成组合的解决方案

时间:2017-05-26 11:29:45

标签: function functional-programming ocaml combinations

source code

我试图理解用于生成从列表的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

2 个答案:

答案 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] )