给定字母["a"; "b"; "c"]
我想将长度为25的所有序列转储到文件中。 (字母可以按顺序重复;它不是排列。)问题是,当我尝试使用以下代码时,我得到Stack overflow during evaluation (looping recursion?)
:
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. *)
有更好的方法吗?我想先生成整个列表,然后将整个列表转储到文件中,但是我是否必须重复生成部分列表然后转储?会有什么懒惰的帮助吗?
为什么发生堆栈溢出? AFAICT,我的generateWords
函数是尾递归的。问题是我生成的words
列表是否太大而无法适应内存?
答案 0 :(得分:6)
您的函数正在编译为tailcalls。我从线性化代码中确认了;从本机编译器中的-dlinear
选项ocamlopt[.opt]
获得。
事实是,你的堆呈指数增长,并且这个方法中有25个字是不可持续的。尝试11个工作正常(并且是我可以处理的最高)。
是的,有更好的方法可以做到这一点。您可以通过查找index of the combination in lexicographical order或使用灰色代码(同一页面)来生成组合。这些只需要存储一个单词,可以并行运行,并且永远不会导致分段错误 - 你可能会使用索引方法溢出,在这种情况下你可以切换到大整数但会牺牲速度,或者格雷码(可能难以并行化,具体取决于格雷码)。
答案 1 :(得分:6)
OCaml优化尾递归,因此您的代码应该可以工作,除了:遗憾的是,标准库的List.map
函数不是尾递归的。堆栈溢出可能发生在其中一个调用中,因为列表变得相当大。
Batteries Included和Jane Street的Core库都提供map
的尾递归版本。尝试其中之一,看看它是否解决了问题。