【问题标题】:For Loop Over a Recursive Call OcamlFor 循环遍历递归调用 Ocaml
【发布时间】:2017-02-08 23:05:50
【问题描述】:

我正在研究 OCaml 中素数分解的实现。我不是函数式程序员;下面是我的代码。素数分解在 prime_part 函数中递归发生。 primes 是从 0 到 num 的素数列表。这里的目标是我可以在 OCaml 解释器中输入 prime_part 并在 n = 20, k = 1 时将其吐出。

2 + 3 + 7
5 + 7

我改编自 OCaml 教程中的 is_primeall_primes。在调用prime_part 之前,需要调用all_primes 以生成直到b 的素数列表。

(* adapted from http://www.ocaml.org/learn/tutorials/99problems.html *)
let is_prime n =
    let n = abs n in
    let rec is_not_divisor d =
      d * d > n || (n mod d <> 0 && is_not_divisor (d+1)) in
    n <> 1 && is_not_divisor 2;;

let rec all_primes a b =
  if a > b then [] else
    let rest = all_primes (a + 1) b in
    if is_prime a then a :: rest else rest;;

let f elem =
    Printf.printf "%d + " elem

let rec prime_part n k lst primes =
    let h elem =
        if elem > k then
            append_item lst elem;
        prime_part (n-elem) elem lst primes in
    if n == 0 then begin
        List.iter f lst;
        Printf.printf "\n";
        ()
    end
    else
        if n <= k then
        ()
        else 
            List.iter h primes;
            ();;

let main num = 
    prime_part num 1 [] (all_primes 2 num)

我很困惑 for 循环的隐蔽性。我看到 List.ittr 是 OCaml 方式,但是如果我为 List.ittr 定义另一个函数,我将无法访问我的变量。我需要访问这些变量以递归调用 prime_part。有什么更好的方法来做到这一点?

我可以用 Ruby 表达我想用 OCaml 完成什么。 n = 任意数,k = 1,lst = [],primes = 素数 0 到 n 的列表

def prime_part_constructive(n, k, lst, primes)
if n == 0
  print(lst.join(' + '))
  puts()
  end
  if n <= k
    return
  end
  primes.each{ |i|
    next if i <= k
    prime_part_constructive(n - i, i, lst+[i], primes)
    }
end

【问题讨论】:

    标签: recursion ocaml primes prime-factoring


    【解决方案1】:

    以下是您的代码中的一些 cmets。

    1. 您可以在 OCaml 中定义嵌套函数。嵌套函数可以访问所有先前定义的名称。因此,您可以使用 List.iter 而不会失去对局部变量的访问权限。

    2. 我看不出您的函数 prime_part_constructive 返回整数值的任何原因。在 OCaml 中返回值 () 会更惯用,称为“单位”。这是因其副作用(例如打印值)而被调用的函数返回的值。

    3. 符号a.(i) 用于访问数组,而不是列表。 OCaml 中的列表和数组并不相同。如果您将 for 替换为 List.iter,您就不必担心这一点。

    4. 要连接两个列表,请使用@ 运算符。 lst.concat 符号在 OCaml 中没有意义。

    更新

    下面是嵌套函数的样子。这个组合函数接受一个数字n 和一个整数列表,然后写出列表中每个元素的值乘以n

    let write_mults n lst =
        let write1 m = Printf.printf " %d" (m * n) in
        List.iter write1 lst
    

    write1 函数是一个嵌套函数。请注意,它可以访问n 的值。

    更新 2

    这是我在编写函数时得到的:

    let prime_part n primes =
        let rec go residue k lst accum =
            if residue < 0 then
                accum
            else if residue = 0 then
                lst :: accum
            else
                let f a p =
                    if p <= k then a
                    else go (residue - p) p (p :: lst) a
                in
                List.fold_left f accum primes
        in  
        go n 1 [] []
    

    它适用于您的示例:

    val prime_part : int -> int list -> int list list = <fun>
    # prime_part 12 [2;3;5;7;11];;
    - : int list list = [[7; 5]; [7; 3; 2]]
    

    请注意,此函数返回分区列表。这比写出来(恕我直言)更有用(和功能)。

    【讨论】:

    • 我在原始帖子中编辑了我的 OCaml 代码以反映您建议的更改。当我将它放入 OCaml 解释器时,它正在轰炸函数 h,因为它无法访问 lst。它给了我一个Unbound value lst 错误。知道为什么会这样吗?
    • 您的函数h 未定义为嵌套函数。如果你想让它访问lst,你需要定义它inside prime_part_constructive
    • 我将它移到函数中(见上文)。但是,解释器说它找不到h。那是因为我在使用它之前引用了它吗?如果是这样,我怎样才能在不破坏prime_part的函数定义的情况下改变它。
    • 它仍然不在函数内部,它现在已经超过了另一边的函数 :-) 我会写一个嵌套函数的例子。
    • 啊哈!我相信我现在将其更正为使用 in 运算符在函数内部。但是,现在我有一个错误This expression has type 'a -&gt; 'b list -&gt; 'c -&gt; 'd but an expression was expected of type int。当我递归调用prime_part 时,解释器在prime_part n 下划线。函数调用中可以有表达式吗?