List 自定义打开并返回累加器

List 自定义打开并返回累加器,list,recursion,f#,tail-recursion,unfold,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

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

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
生成的列表,并以正确的顺序生成它。我想有必要使用continuations来构建这个列表,但我对函数式编程非常陌生,还没有完全理解continuations;我能想象的所有替代方案都会将累加器放在结果列表中,或者不允许函数返回累加器

有没有办法做到这一点


由于这是一个个人学习练习,我更喜欢一个解释如何做的答案,而不是简单地给出完整的代码。

没有
列表的方法。rev
是传递一个函数,而不是
resultList
参数。让我们调用该函数
buildResultList
。在每一步中,该函数都会获取已经构建的列表尾部,预先添加当前项,然后将其传递给上一步中的函数,该函数将追加上一项,并将其传递给上一步中的函数,依此类推。此链中的最后一个函数将在列表的第一个项目前加上前缀。整个递归循环的结果将是链的最后一个函数(它调用所有前面的函数),然后您将使用空列表作为参数调用该函数。恐怕这是我不需要写代码就可以做到的

然而问题是,对于任何“更好”的定义来说,这都不会更好。由于计算是“向前”进行的,而结果列表是“向后”构建的(head::tail,Lisp风格),因此必须在某个地方累积结果。在您的代码中,您正在将其累积到一个临时列表中,但是如果您将其修改为使用continuations,那么您将在堆中将其累积为一系列闭包,这些闭包在链中相互引用。有人可能会争辩说,从本质上讲,它将是同一个列表,只是模糊了

您可以尝试的另一种方法是使用惰性序列:构建一个递归的
seq
计算,它将
生成当前项,然后
生成本身。然后可以枚举该序列,它不需要“临时”存储但是,如果您仍然希望在最后得到一个列表,您必须通过
list.ofSeq
将序列转换为一个列表,然后猜猜这将如何实现?理论上,从纯数学的角度来看,
List.ofSeq
将以完全相同的方式实现:首先构建临时列表,然后将其反转。但是F#library在这里作弊:它以一种可变的方式构建列表,因此不必反转

最后,由于这是一个学习练习,您还可以自己实现等效的惰性序列。现在,标准的.NET序列(aka
IEnumerable
,这是
Seq
的别名)本质上是可变的:每次移动到下一项时,您都在更改迭代器的内部状态。你可以这样做,或者,本着学习的精神,你可以做一个不变的等价物。这几乎就像一个列表(即head::tail),不同的是,由于它是惰性的,“tail”必须是一个承诺,而不是实际的序列,因此:

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

由于Fyodor Soikin的回答,LazySeqStep的结果函数如下:

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

继续是一条路(如果你真的想避免这种无害的
List.rev
)。延续只是一个函数,它将生成结果(这里是一个列表)。我不知道答案不给出(代码)会是什么样子。我知道
List.rev
是无害的,我只是想学习如何不给出答案。代码是受欢迎的,我只是希望有一个解释,让我自己尽可能多地做它