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