【问题标题】:Ocaml 99 problems: can't understand the solution for generating combinationsOcaml 99 问题:无法理解生成组合的解决方案
【发布时间】:2017-10-27 06:47:46
【问题描述】:

https://ocaml.org/learn/tutorials/99problems.html

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

【问题讨论】:

    标签: function functional-programming ocaml combinations


    【解决方案1】:

    OCaml 中的函数通常是柯里化的,这意味着通过接受一个参数并返回一个接受下一个参数的函数来表示多个参数函数(依此类推)。 OCaml 有一些语法糖来使这更易于阅读:let f x y = ...let f = fun x -> fun y -> ... 的缩写。

    通常程序员通过一次传递所有参数来使用这些函数,但也有可能只传递一个并返回一个“部分应用”的函数作为结果。这就是 emit 正在发生的事情。

    因此您可以将let emit_new x = emit (h :: x) 解读为定义emit 的版本,其中第一个参数已经提供。

    【讨论】:

      【解决方案2】:

      这里缺少的一点是,由于柯里化和一等函数,函数的参数数量并不像你想象的那么严格。

      在这种特殊情况下,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 具有 for 类型 '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
      

      附言: 作为对函数参数数量主题的更高级的评论,请注意,即使是“可变参数”函数也完全可能在 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] )
      

      【讨论】:

      • 感谢您的详细回答和代码解释
      猜你喜欢
      • 2020-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-30
      • 2021-09-28
      • 1970-01-01
      • 2020-08-31
      相关资源
      最近更新 更多