IEnumerable<T> 跳过无限序列
IEnumerable<T> Skip on unlimited sequence
我有一个使用 BigInteger 的斐波那契数列的简单实现:
internal class FibonacciEnumerator : IEnumerator<BigInteger>
{
private BigInteger _previous = 1;
private BigInteger _current = 0;
public void Dispose(){}
public bool MoveNext() {return true;}
public void Reset()
{
_previous = 1;
_current = 0;
}
public BigInteger Current
{
get
{
var temp = _current;
_current += _previous;
_previous = temp;
return _current;
}
}
object IEnumerator.Current { get { return Current; }
}
}
internal class FibonacciSequence : IEnumerable<BigInteger>
{
private readonly FibonacciEnumerator _f = new FibonacciEnumerator();
public IEnumerator<BigInteger> GetEnumerator(){return _f;}
IEnumerator IEnumerable.GetEnumerator(){return GetEnumerator();}
}
这是一个无限序列,因为MoveNext()
总是returns为真。
调用时使用
var fs = new FibonacciSequence();
fs.Take(10).ToList().ForEach(_ => Console.WriteLine(_));
输出符合预期 (1,1,2,3,5,8,...)
我想要 select 10 个项目,但从第 100 个位置开始。我尝试通过
调用它
fs.Skip(100).Take(10).ToList().ForEach(_ => Console.WriteLine(_));
但这不起作用,因为它从头开始输出十个元素(即输出又是 1,1,2,3,5,8,...)。
我可以通过调用SkipWhile
跳过它
fs.SkipWhile((b,index) => index < 100).Take(10).ToList().ForEach(_ => Console.WriteLine(_));
从第 100 个元素开始正确输出 10 个元素。
在枚举器中是否有其他 needs/can 可以实现 Skip(...)
的东西?
Skip(n)
不访问 Current
,它只是调用 MoveNext()
n
次。
所以需要在MoveNext()
进行自增,即the logical place for that operation anyway:
Current does not move the position of the enumerator, and consecutive calls to Current return the same object until either MoveNext or Reset is called.
将您的逻辑移至 MoveNext
:
public bool MoveNext()
{
var temp = _current;
_current += _previous;
_previous = temp;
return true;
}
public void Reset()
{
_previous = 1;
_current = 0;
}
public BigInteger Current
{
get
{
return _current;
}
}
Skip(10) 只是调用 MoveNext
10 次,然后调用 Current
。在 MoveNext
中完成操作也比在当前
中完成操作更合乎逻辑。
CodeCaster 的回答很准确——我只是想指出,您并不真的需要为这样的事情实现自己的可枚举:
public IEnumerable<BigInteger> FibonacciSequence()
{
var previous = BigInteger.One;
var current = BigInteger.Zero;
while (true)
{
yield return current;
var temp = current;
current += previous;
previous = temp;
}
}
编译器将为您创建枚举数和可枚举数。对于像这样的简单枚举,区别并不是那么大(你只是避免了大量的样板),但如果你真的需要比简单的递归函数更复杂的东西,那就有很大的不同了。
我有一个使用 BigInteger 的斐波那契数列的简单实现:
internal class FibonacciEnumerator : IEnumerator<BigInteger>
{
private BigInteger _previous = 1;
private BigInteger _current = 0;
public void Dispose(){}
public bool MoveNext() {return true;}
public void Reset()
{
_previous = 1;
_current = 0;
}
public BigInteger Current
{
get
{
var temp = _current;
_current += _previous;
_previous = temp;
return _current;
}
}
object IEnumerator.Current { get { return Current; }
}
}
internal class FibonacciSequence : IEnumerable<BigInteger>
{
private readonly FibonacciEnumerator _f = new FibonacciEnumerator();
public IEnumerator<BigInteger> GetEnumerator(){return _f;}
IEnumerator IEnumerable.GetEnumerator(){return GetEnumerator();}
}
这是一个无限序列,因为MoveNext()
总是returns为真。
调用时使用
var fs = new FibonacciSequence();
fs.Take(10).ToList().ForEach(_ => Console.WriteLine(_));
输出符合预期 (1,1,2,3,5,8,...)
我想要 select 10 个项目,但从第 100 个位置开始。我尝试通过
调用它fs.Skip(100).Take(10).ToList().ForEach(_ => Console.WriteLine(_));
但这不起作用,因为它从头开始输出十个元素(即输出又是 1,1,2,3,5,8,...)。
我可以通过调用SkipWhile
跳过它fs.SkipWhile((b,index) => index < 100).Take(10).ToList().ForEach(_ => Console.WriteLine(_));
从第 100 个元素开始正确输出 10 个元素。
在枚举器中是否有其他 needs/can 可以实现 Skip(...)
的东西?
Skip(n)
不访问 Current
,它只是调用 MoveNext()
n
次。
所以需要在MoveNext()
进行自增,即the logical place for that operation anyway:
Current does not move the position of the enumerator, and consecutive calls to Current return the same object until either MoveNext or Reset is called.
将您的逻辑移至 MoveNext
:
public bool MoveNext()
{
var temp = _current;
_current += _previous;
_previous = temp;
return true;
}
public void Reset()
{
_previous = 1;
_current = 0;
}
public BigInteger Current
{
get
{
return _current;
}
}
Skip(10) 只是调用 MoveNext
10 次,然后调用 Current
。在 MoveNext
中完成操作也比在当前
CodeCaster 的回答很准确——我只是想指出,您并不真的需要为这样的事情实现自己的可枚举:
public IEnumerable<BigInteger> FibonacciSequence()
{
var previous = BigInteger.One;
var current = BigInteger.Zero;
while (true)
{
yield return current;
var temp = current;
current += previous;
previous = temp;
}
}
编译器将为您创建枚举数和可枚举数。对于像这样的简单枚举,区别并不是那么大(你只是避免了大量的样板),但如果你真的需要比简单的递归函数更复杂的东西,那就有很大的不同了。