【问题标题】:IEnumerable<T> Skip on unlimited sequenceIEnumerable<T> 跳过无限序列
【发布时间】:2015-11-30 14:37:18
【问题描述】:

我有一个使用 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()总是返回true。

调用时使用

var fs = new FibonacciSequence();
fs.Take(10).ToList().ForEach(_ => Console.WriteLine(_));

输出如预期(1,1,2,3,5,8,...)

我想选择 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 个元素。

还有什么需要/可以在枚举器中实现以使Skip(...) 工作?

【问题讨论】:

  • 如果您使用正确的 c# 版本(不确定哪个 atm),您可以使用 yieldyield return,这为我节省了很多精力,并且无需定义新的枚举器类bc 它为你完成了。
  • 在您的情况下,多次访问 Current 会产生不同的结果。不应该。
  • 呃,有副作用的吸气剂!讨厌。

标签: c# linq fibonacci skip


【解决方案1】:

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;
  }
}

编译器将为您创建枚举器和枚举器。对于像这样的简单枚举,差异并没有那么大(您只需避免大量样板文件),但如果您确实需要比简单的递归函数更复杂的东西,它会产生巨大的差异。

【讨论】:

  • 这个。收益回报是您的朋友。
【解决方案2】:

将您的逻辑移至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 中完成操作而不是当前操作也更符合逻辑。

【讨论】:

    【解决方案3】:

    Skip(n) 不访问Current,它只是调用MoveNext() n 次。

    所以你需要在MoveNext()中执行增量,也就是the logical place for that operation anyway

    Current 不会移动枚举数的位置,并且对 Current 的连续调用会返回相同的对象,直到调用 MoveNext 或 Reset。

    【讨论】:

      猜你喜欢
      • 2010-11-18
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多