自定义展开返回累加器

时间:2016-09-23 12:34:06

标签: list recursion f# tail-recursion unfold

我尝试创建一个自定义展开函数,返回其最后一个累加器值,如:

val unfold' : generator:('State -> ('T * 'State) option) -> state:'State -> 'T list * 'State

我设法做了以下事情:

let unfold' generator state =
    let rec loop resultList state =
        match generator state with
        | Some (value, state) -> loop (value :: resultList) state
        | None -> (List.rev resultList), state
    loop [] state

但是我想避免List.rev结果列表并使用正确的顺序生成它。我想有必要使用continuation来构建列表,但我对函数式编程很陌生,并且还没有设法将我的思想包围在延续中;我能想象的所有替代方案都会将累加器放在结果列表中,或者不允许函数返回它。

有没有办法做到这一点?

由于这是一项个人学习练习,我更倾向于回答如何解释,而不是简单地提供完整的代码。

2 个答案:

答案 0 :(得分:5)

没有List.rev的方法是传递函数而不是resultList参数。让我们调用该函数buildResultList。在每个步骤中,此函数将获取列表的已构建尾部,在当前项目前添加,然后将其传递给上一个步骤中的函数,该步骤将附加 previous < / em> item,将其传递给 previous-previous 步骤中的函数,依此类推。此链中的最后一个函数将在列表中添加第一个项目。整个递归循环的结果将是链的 last 函数(它调用所有先前的函数),然后您将使用空列表作为参数调用。我担心,只要编写代码就可以清楚地看到这一点。

然而,问题是,对于&#34;更好的&#34;的任何定义,这都不会更好。由于计算正在进行&#34;转发&#34;,并且生成了结果列表&#34;向后&#34; (head :: tail,Lisp-style),你来累积结果某处。在您的代码中,您将其累积在临时列表中,但如果您将其修改为使用continuation,那么您将在堆上将其作为一系列在链中相互引用的闭包累积。有人可能会争辩说,它本质上是同一个列表,只是模糊不清。

您可以尝试的另一种方法是使用延迟序列:构建递归seq计算,它将yield当前项目,然后yield!本身。然后你可以枚举这个序列,它不会需要一个临时的&#34;存储。 ,如果您仍希望在最后获得列表,则必须通过List.ofSeq将序列转换为列表,然后猜猜这是怎么回事将要执行?从理论上讲,从纯粹的数学角度来看,List.ofSeq将以完全相同的方式实现:首先构建临时列表然后反转它。但是F#库在这里作弊:它以可变的方式构建列表,因此它不必反转。

最后,由于这是一个学习练习,你也可以自己实现相当于懒惰的序列。现在,标准的.NET序列(又名IEnumerable<_>,这是Seq<_>的别名)本质上是可变的:你正在改变内部状态每次移动到下一个项目时都会使用迭代器。你可以做到这一点,或者,本着学习的精神,你可以做一个不可变的等价物。这将是几乎像列表(即head :: tail),除了它,因为它 lazy ,&#34; tail&#34;必须是承诺而不是实际的顺序,所以:

type LazySeq<'t> = LazySeq of (unit -> LazySeqStep<'t>)
and LazySeqStep<'t> = Empty | Cons of head: 't * tail: LazySeq<'t>

枚举的方法是调用函数,它将返回下一个项目加上序列的尾部。然后你可以将你的展开编写为一个函数,它将当前项作为head返回,然后只返回自己包含在tail的闭包中。事实证明,非常简单。

答案 1 :(得分:2)

感谢Fyodor Soikin的回答,这是最终的功能:

let unfold' generator state =
    let rec loop build state =
        match generator state with
        | Some (value, state) -> loop (fun newValue -> build (value :: newValue)) state
        | None -> (build []), state
    loop id state