Functional programming 为什么即使在OCaml中使用尾部递归,我仍然会得到stackoverflow?

Functional programming 为什么即使在OCaml中使用尾部递归,我仍然会得到stackoverflow?,functional-programming,ocaml,Functional Programming,Ocaml,我编写了一个函数,它在OCaml中生成一个随机整数的列表 当我试图生成10000整数时,它给出了异常:RangeError:超出了最大调用堆栈大小。错误 但是,我相信这个函数,我使用了尾部递归,它不应该给出堆栈溢出错误,对吗 有什么想法吗?来自 val append:'a list->'a list->'a list 把两张清单连在一起。与中缀运算符@的功能相同。非尾部递归(第一个参数的长度)。@运算符也不是尾部递归的 所以导致溢出的不是你的函数,而是@函数。但是,由于您只关心生成一个无序列

我编写了一个函数,它在
OCaml
中生成一个
随机整数的列表



当我试图生成
10000
整数时,它给出了
异常:RangeError:超出了最大调用堆栈大小。
错误

但是,我相信这个函数,我使用了
尾部递归
,它不应该给出
堆栈溢出
错误,对吗

有什么想法吗?

来自

val append:'a list->'a list->'a list 把两张清单连在一起。与中缀运算符@的功能相同。非尾部递归(第一个参数的长度)。@运算符也不是尾部递归的

所以导致溢出的不是你的函数,而是
@
函数。但是,由于您只关心生成一个无序列表,所以没有理由在列表的末尾追加内容。即使
@
操作符是尾部递归的,list append仍然是O(n)。然而,列表前缀是O(1)。因此,如果将新的随机数粘贴在列表的前面,可以避免溢出(并使函数更快):

如果您关心订单(不确定原因),那么只需在末尾粘贴List.rev:

List.rev (create n []);;

另外,您不应该在函数中调用
Random.self_init
,因为:

  • 函数的用户可能希望控制种子以获得可复制的结果(测试、共享结果…)

  • 这可能会用不太随机的熵源重置种子,您可能只想执行一次


Hm,对我来说很好,即使有10万美元。但是,您的函数效率极低,因为每次都要在累加器的末尾追加,这将提供O(n^2)运行时。@AndreasRossberg您使用的是变体标准库吗?Jane Street Core适用于我,但stdlib
@
不是尾部递归函数。该函数的定义在许多方面似乎都有很大的错误——例如,名为“shuffle”的函数通常可以保证某种形式的分布公平性/一致性,这与您的函数完全不同,因此它可能是错误的。你能提供更多关于你期望这个函数做什么的高级细节吗?更正:我实际上只运行了10000次,而不是100000次(对不起!)。但这确实有效。我的安装没有什么不标准的地方。也许堆栈空间在不同的体系结构中有所不同?那么,如果我想保持元素的有序性,那么我应该使用什么来生成尾部递归呢。为了保持元素的有序性,请反向创建列表,然后在完全构建后将其反向创建。@JacksonTale我编辑了一些关于如何克服此问题的详细信息。谢谢。我要求订购,因为我只是一名OCaml学习者,所以我想了解有关OCaml的基本知识。这也是为什么我不尝试使用更好的库。@JacksonTale:您发布了很多关于OCaml的问题,这很好,但我们从中看到,您坚持使用其他语言学习的编程风格。我强烈建议您学习OCaml本身的风格和思维方式。欢迎使用StackOverflow。你所说的话有很多洞察力,几乎不适合评论,但由于你没有直接回答问题,一个评论(或几个评论)就是你在这里的反应方式。除了你不能评论1代表。奇怪的规则,我知道(我没有制定它们)。在这种情况下(当你不能发表评论或你所说的内容不符合评论的大小限制时),一个技巧是在评论横向方面之前简洁地回答问题。谢谢你的提示。我查阅了FAQ,了解如何在这种情况下发表评论,最后使用了答案表单。在回答真正的问题之前,应该允许人们发表评论。我猜这条规则来自于推动实际解决方案而非意见和讨论的意愿。
let create_shuffled_int_list n = 
  Random.self_init;
  let rec create n' acc =
    if n' = 0 then acc
    else 
      create (n'-1) (Random.int (n/2) :: acc)
  in 
  create n [];;
List.rev (create n []);;