显然,每次调用 HeadAndTail 都应该再次枚举序列(除非使用了某种缓存)。例如,考虑以下情况:
var a = HeadAndTail(sequence);
Console.WriteLine(HeadAndTail(a.Tail).Tail);
//Element #2; enumerator is at least at #2 now.
var b = HeadAndTail(sequence);
Console.WriteLine(b.Tail);
//Element #1; there is no way to get #1 unless we enumerate the sequence again.
出于同样的原因,HeadAndTail 不能实现为单独的 Head 和 Tail 方法(除非您甚至希望第一次调用 Tail 再次枚举序列,即使它已经通过调用 Head 枚举)。
此外,HeadAndTail 不应返回 IEnumerable 的实例(因为它可能被枚举多次)。
这给我们留下了唯一的选择:HeadAndTail 应该返回 IEnumerator,并且,为了让事情更明显,它应该接受 IEnumerator 作为好吧(我们只是将 GetEnumerator 的调用从 HeadAndTail 内部移到外部,以强调它只能一次性使用。
现在我们已经确定了需求,实现非常简单:
class HeadAndTail<T> {
public readonly T Head;
public readonly IEnumerator<T> Tail;
public HeadAndTail(T head, IEnumerator<T> tail) {
Head = head;
Tail = tail;
}
}
static class IEnumeratorExtensions {
public static HeadAndTail<T> HeadAndTail<T>(this IEnumerator<T> enumerator) {
if (!enumerator.MoveNext()) return null;
return new HeadAndTail<T>(enumerator.Current, enumerator);
}
}
现在可以这样使用了:
Console.WriteLine(sequence.GetEnumerator().HeadAndTail().Tail.HeadAndTail().Head);
//Element #2
或者在这样的递归函数中:
TResult FoldR<TSource, TResult>(
IEnumerator<TSource> sequence,
TResult seed,
Func<TSource, TResult, TResult> f
) {
var headAndTail = sequence.HeadAndTail();
if (headAndTail == null) return seed;
return f(headAndTail.Head, FoldR(headAndTail.Tail, seed, f));
}
int Sum(IEnumerator<int> sequence) {
return FoldR(sequence, 0, (x, y) => x+y);
}
var array = Enumerable.Range(1, 5);
Console.WriteLine(Sum(array.GetEnumerator())); //1+(2+(3+(4+(5+0)))))