OCaml中的递归函数

时间:2009-12-23 13:03:55

标签: recursion ocaml stack-overflow

我有一点问题:我想用OCaml解决this problem, 所以我尝试了这个 - >

-> let rec somme x = if ( nor (bool_of_int (x mod 3)) (bool_of_int (x mod 5))) then x + (somme x-1) else (somme x-1) ;;

val somme : int -> int = <fun>

-> somme 1000 ;;

Stack overflow during evaluation (looping recursion?).

我做错了什么?


我尝试了新代码:

let somme2 x = if (( nor (bool_of_int (x mod 3)) (bool_of_int (x mod 5)))) then x + somme (x-1) else somme (x-1) ;;

let somme x = if x = 0 then x else somme2 x ;;

同样的错误。

7 个答案:

答案 0 :(得分:8)

1)你的记忆永远不会停止在开头添加if x == 0 then 0 else ...之类的测试

2)你没有在x-1附近加上括号,所以ocaml会读(somme x)-1。改为写somme (x-1)

答案 1 :(得分:2)

正如其他人所指出的那样,你应该对基本情况进行测试。您可以使用模式匹配:

match x with
| 0 -> ...
| n -> ...;;

函数式语言通常与数学符号紧密相关,模式匹配实际上与您在纸上编写方程式的方式非常相似。

答案 2 :(得分:2)

这是解决问题的简单方法,也许它可以帮助您提高OCaml技能

(*generates a list of the multiples of num and stops at max*)
let gen_mult num max=

  let rec gen i=

  if i*num>=max then []

  else (i*num)::gen (i+1)

  in gen 1;;

let m3=gen_mult 3 1000;;

let m5=gen_mult 5 1000;;

(*sums the multiples of 3*)
let s3=List.fold_left (fun acc x->x+acc) 0 m3;;

(*sums the multiples of 5 except those of 3*)
let s5=List.fold_left (fun acc x->if x mod 3=0 then acc else x+acc) 0 m5;;

let result=s3+s5;;

答案 3 :(得分:2)

循环没有终止条件。修复你的代码:

let rec somme1 x = 
  if x <= 0
    then 0
    else if ((x mod 3) = 0) or ((x mod 5) = 0)
      then x + (somme1 (x-1))
      else somme1 (x-1);;

somme1 1000;;

为了改进你的代码,你要使你的函数拖尾递归。

let rec somme2 x accum = 
  if x <= 0
    then accum
    else if ((x mod 3) = 0) or ((x mod 5) = 0)
      then somme2 (x-1) (accum+x)
      else somme2 (x-1) accum

somme2 1000 0;;

两个版本之间的区别在于,在尾递归的情况下,递归调用的结果与函数的结果完全相同,因此不需要存储中间状态来完成后的计算。调用递归函数。当您致电somme1 1000;;时,由于(1000 mod 5 == 0) or (1000 mod 3 == 0)评估true,您会收到递归调用1000 + (somme1 999),要完成,需要递归调用999 + (somme1 998)。编译器必须将数字1000999保持在堆栈中,直到somme1完成执行,而不是(无终止条件),因此您的堆栈会填满尝试存储1000 + (999 + (996 + (995 + ...

这相当于((((0 + 1000) + 999) + 996) + 995) + ...,但在这种情况下,没有处理递归调用结果所需的中间值(也就是说,递归调用的返回值与返回值相同)函数本身的值),因此不需要额外的堆栈空间。第二个版本以这种方式工作。如果它与第一个有相同的错误,它将不会用完堆栈,但会继续无限期地执行。这被认为是一种改进。 : - )

答案 4 :(得分:1)

我知道关于OCaml的zip,但看起来你的代码没有递归的终止条件。

答案 5 :(得分:0)

我在上面的代码中看不到任何终止条件?通常情况下,我希望递归停止x的某个值,可能是0或1.你的代码似乎没有包含这样的任何规定。

请记住,我对Neam承认的OCaml了解得多。

答案 6 :(得分:0)

尝试使用模式匹配和过滤参数 语法:

let f a= match a with
| a when (a=..)  -> ...
| a when (a=..)-> ...
| _ -> ...;;

let f = function
   p1 -> expr1
 | p2 -> expr2
 | p3 -> ...;;

解决方案:


let mult_3or5  a = match a with
  a when ((a mod 3=0)||(a mod 5=0)) ->true
 |_  ->false;;

let rec somme_mult_3or5 = function 
    a when (a=0) -> failwith "Indice invalide"    
   |a when (a=1) -> 0
   |a -> if (mult_3or5 (a-1)=true) then ((a-1)+ somme_mult_3or5 (a-1)) 
         else somme_mult_3or5 (a-1);;