Ocaml 在不破坏堆栈的情况下生成素数

Ocaml 在不破坏堆栈的情况下生成素数,ocaml,primes,Ocaml,Primes,我正在学习OCaml(所以请原谅我的风格),并试图编写一个函数,生成一个素数列表,其上限为。我已经用几种不同的方法做到了这一点,所有这些方法都有效,直到你将它们扩展到一个相对较高的上限 如何更改这些(任意一个)以使递归不会填满堆栈?我以为我的while循环版本可以实现这一点,但显然不行 发电机 Eratosthenes v2的递归筛 设素数max= 让我们来测试一下= 设h=List.hd toTest 和t=List.tl toTest in 让我们把x=(x模h 0)分为 让非分隔符=Lis

我正在学习OCaml(所以请原谅我的风格),并试图编写一个函数,生成一个素数列表,其上限为。我已经用几种不同的方法做到了这一点,所有这些方法都有效,直到你将它们扩展到一个相对较高的上限

如何更改这些(任意一个)以使递归不会填满堆栈?我以为我的while循环版本可以实现这一点,但显然不行

发电机 Eratosthenes v2的递归筛
设素数max=
让我们来测试一下=
设h=List.hd toTest
和t=List.tl toTest in
让我们把x=(x模h 0)分为
让非分隔符=List.filter不在
如果非分频器=[],则[h]
中的else(h::筛选非分隔符)
让rec范围为a到b=
如果a>b,则[]
否则a::范围(a+1)b in
设p=最大范围2英寸
p筛;;
而埃拉托斯烯的环筛
设素数max=
让rec范围为a到b=
如果a>b,则[]
否则a::范围(a+1)b in
让尾=参考(最大范围2)
和p=ref[]in
虽然尾
让h=List.hd!尾
t=List.tl!尾随
让我们把x=(x模h 0)分为
将newTail=ref(List.filter不提供t)放入
尾巴:=!纽泰尔;
p:=h:!P
完成;
!P

堆栈溢出是因为范围函数不是尾部递归函数。一个有效的方法是,例如

  let rec range store a b =
    if a > b then store
    else range (a :: store) (a + 1) b
  in

  let p = List.rev (range [] 2 max) in
使用该定义和格式行,可以

$ ocamlopt -o primes2 primes2.ml
$ ./primes2
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
...
既然你在学习,我也会给你一些不请自来的风格评论:)

  • 不要使用hd和tl。更喜欢模式匹配。然后编译器可以告诉你你错过的案例。例如

    让rec筛子进行测试= 设h=List.hd toTest 和t=List.tl toTest in

会是

let rec sieve found = function
  | h :: t -> ...
  | [] -> Error handling...
  • 不要使用x=[]。使用图案修补

    将x与 | [] -> ... |h::t->

  • 使用匿名函数,而不是在

  • 谨慎使用命令式功能


    • 你声称是埃拉托斯坦筛的算法实际上不是;他们使用试除法而不是筛选法,通过寻找余数(mod运算符)与零的比较,很容易发现这一点。下面是Eratosthenes筛的一个简单实现,使用伪代码而不是Ocaml,因为我已经很久没有编写Ocaml代码了:

      function primes(n)
          sieve := makeArray(2..n, True)
          for p from 2 to n
              if sieve[p]
                  output p
                  for i from p*p to n step p
                      sieve[i] := False
      

      这可以进一步优化,尽管对于n=2000000这样的小限制,这样做没有什么意义;在任何情况下,筛子将比您正在使用的试用版快得多。如果您对使用素数编程感兴趣,我在我的博客上谦虚地推荐这一点。

      对于您使用匿名函数的建议,我完全不同意
      List.exists hasDivisor p
      是清除的,
      List.filter不提供t
      。具有匿名函数的版本不可用。名称是文档。
      x*x+y*y
      中的子表达式
      x*x
      不应该有名字,但是
      (funx->(xmodh0))
      应该有(快速:它的意图是什么?)我将强调简短。这个特定函数体的意图与英文文本一样容易确定,并且不需要再添加一个需要记住的名称(尽管只需要一行代码)。
        let rec range store a b =
          if a > b then store
          else range (a :: store) (a + 1) b
        in
      
        let p = List.rev (range [] 2 max) in
      
      $ ocamlopt -o primes2 primes2.ml
      $ ./primes2
      2
      3
      5
      7
      11
      13
      17
      19
      23
      29
      31
      37
      41
      43
      47
      53
      59
      ...
      
      let rec sieve found = function
        | h :: t -> ...
        | [] -> Error handling...
      
      function primes(n)
          sieve := makeArray(2..n, True)
          for p from 2 to n
              if sieve[p]
                  output p
                  for i from p*p to n step p
                      sieve[i] := False