在ocaml中生成大序列字母时堆栈溢出

在ocaml中生成大序列字母时堆栈溢出,ocaml,stack-overflow,tail-recursion,Ocaml,Stack Overflow,Tail Recursion,给定一个字母表,我想把所有长度为25的序列转储到一个文件中。(字母可以按顺序重复;这不是排列。)问题是,当我尝试使用以下代码时,在求值过程中(循环递归?)堆栈溢出: let addAlphabetToPrefix alphabet prefix = List.map (function letter -> (prefix ^ letter)) alphabet;; let rec generateWords alphabet counter words = if counter

给定一个字母表,我想把所有长度为25的序列转储到一个文件中。(字母可以按顺序重复;这不是排列。)问题是,当我尝试使用以下代码时,在求值过程中(循环递归?)堆栈溢出:

let addAlphabetToPrefix alphabet prefix =
  List.map (function letter -> (prefix ^ letter)) alphabet;;

let rec generateWords alphabet counter words =
  if counter > 25 then
    words
  else
    let newWords = List.flatten(List.map (function word -> addAlphabetToPrefix alphabet word) words) in 
    generateWords alphabet (counter + 1) newWords;;

generateWords ["a"; "b"; "c"] 0 [""];; (* Produces a stack overflow. *)
有更好的方法吗?我想先生成整个列表,然后将整个列表转储到一个文件中,但是我必须重复生成部分列表,然后转储吗?做些懒惰的事情会有帮助吗


为什么会发生堆栈溢出?最后,我的
generateWords
函数是尾部递归函数。我正在生成的
单词列表是否太大,无法放入内存?

您的函数正在编译为tailcalls。我从线性化代码中确认;从本机编译器的
-dlinear
选项,
ocamlopt[.opt]
中获取

事实是,你的堆正在成倍增长,而25个单词在这种方法中是不可持续的。尝试11次效果很好(这是我能应付的最高级别)


是的,有更好的方法。您可以通过查找或使用灰色代码(同一页)生成组合。这些操作只需要存储一个字,可以并行运行,并且不会导致分段错误——不过,使用索引方法可能会导致数据溢出,在这种情况下,您可以切换到大整数,但会牺牲速度或灰码(根据灰码的不同,可能难以并行).

OCaml优化了尾部递归,因此您的代码应该可以工作,但:不幸的是,标准库的
List.map
函数不是尾部递归函数。堆栈溢出可能发生在其中一个调用中,因为您的列表变得相当大


Jane Street的核心库都提供了
map
的尾部递归版本。试试其中一个,看看它是否解决了问题。

ocaml优化了尾部递归吗?@Jeff:有趣!我真的不知道ocaml是关于什么的。就这样,我所知道的语言似乎并不费心去优化尾部递归:-)您应该
#跟踪
generateWords
函数,看看它是否对这个主题有帮助。我认为这只是因为你在这里生成了一个巨大的列表(可能是26^25个单词),所以你的内存自然会耗尽。加上你是递归生成的,会有很多中间结果;我忘了。我只是在看他的声明函数的尾部递归。但是,他/她仍然需要处理具有该大小字长的内存问题。在这种情况下,还可以使用
List.rev_map
(而不是更改
List.map
实现)。