我有一个F#函数,它返回跳转n的模式中从0开始的数字列表,选择n,跳过n,选择n ...直到限制。例如,输入2的这个函数将返回[2, 3, 6, 7, 10, 11...]
。F中的缓慢递归递归#
最初我实现此作为如下面的非尾递归函数:
let rec indicesForStep start blockSize maxSize =
match start with
| i when i > maxSize -> []
| _ -> [for j in start .. ((min (start + blockSize) maxSize) - 1) -> j] @ indicesForStep (start + 2 * blockSize) blockSize maxSize
以为尾递归是可取的,我重新实现它使用的蓄能器列表如下:
let indicesForStepTail start blockSize maxSize =
let rec indicesForStepInternal istart accumList =
match istart with
| i when i > maxSize -> accumList
| _ -> indicesForStepInternal (istart + 2 * blockSize) (accumList @ [for j in istart .. ((min (istart + blockSize) maxSize) - 1) -> j])
indicesForStepInternal start []
然而,当我在单声道下用参数1,1和20,000(即应该返回[1, 3, 5, 7...]
高达20,000)时,在fsi下运行它时,尾递归版本明显比第一版本慢(12秒与亚秒相比)。
为什么尾递归版本较慢?是因为列表串联?它是一个编译器优化?我有没有实现它尾递归?
我也觉得好像我应该使用高阶函数来做到这一点,但我不确定如何去做。
不幸的是,我没有时间来提供替代的代码,但几个简单的观察:1)它是尾递归,2)名单追加为O(N),因此效率低下。我建议反转(下移)你的列表理解,把它归结为accumList,并在你返回第一个模式匹配之前反转accumList。 – Daniel 2011-03-28 14:37:52
非常感谢大家;这些答案非常有帮助,信息量大,教育程度也很高。 – Gnat 2011-03-29 14:20:06