【问题标题】:IEnumerable batching discrepancy in Count() valueCount() 值中的 IEnumerable 批处理差异
【发布时间】:2017-03-26 07:10:00
【问题描述】:

我正在尝试将IEnumerable<T> 分成相等的子集,并遇到了以下解决方案:

  1. MoreLinq Nuget 库 Batch,这里详细介绍其实现:

    MoreLinq - Batch,下面贴源码:

    public static IEnumerable<TResult> Batch<TSource, TResult>(this   
      IEnumerable<TSource> source, int size,
            Func<IEnumerable<TSource>, TResult> resultSelector)
     {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
        if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));
          return BatchImpl(source, size, resultSelector);
     }
    
    private static IEnumerable<TResult> BatchImpl<TSource, TResult> (this IEnumerable<TSource> source, int       
              size,Func<IEnumerable<TSource>, TResult> resultSelector)
    {
        Debug.Assert(source != null);
        Debug.Assert(size > 0);
        Debug.Assert(resultSelector != null);
    
       TSource[] bucket = null;
       var count = 0;
    
    foreach (var item in source)
    {
        if (bucket == null)
        {
            bucket = new TSource[size];
        }
    
        bucket[count++] = item;
    
        // The bucket is fully buffered before it's yielded
        if (count != size)
        {
            continue;
        }
    
        // Select is necessary so bucket contents are streamed too
        yield return resultSelector(bucket);
    
        bucket = null;
        count = 0;
    }
    
    // Return the last bucket with all remaining elements
    if (bucket != null && count > 0)
    {
        Array.Resize(ref bucket, count);
            yield return resultSelector(bucket);
    }
    }
    
  2. 以下链接提供了另一种最佳解决方案(内存效率更高):

    IEnumerable Batching,下面贴源码:

    public static class BatchLinq
    {
         public static IEnumerable<IEnumerable<T>> CustomBatch<T>(this IEnumerable<T> source, int size)
        {
          if (size <= 0)
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
    
         using (IEnumerator<T> enumerator = source.GetEnumerator())
            while (enumerator.MoveNext())
               yield return TakeIEnumerator(enumerator, size);
       }
    
       private static IEnumerable<T> TakeIEnumerator<T>(IEnumerator<T> source, int size)
      {
          int i = 0;
          do
              yield return source.Current;
          while (++i < size && source.MoveNext());
      }
    }
    

两种解决方案都提供IEnumerable&lt;IEnumerable&lt;T&gt;&gt; 的最终结果。

我在以下代码中发现了差异:

var result = Fetch IEnumerable&lt;IEnumerable&lt;T&gt;&gt; 来自上述建议的任一方法

result.Count(),导致不同的结果,它对于 MoreLinq Batch 是正确的,但对于另一个不正确,即使结果正确且两者相同

考虑以下示例:

IEnumerable<int> arr = new int[10] {1,2,3,4,5,6,7,8,9,10};

For a Partition size 3

arr.Batch(3).Count(), will provide result 4 which is correct

arr.BatchLinq(3).Count(), will provide result 10 which is incorrect

即使提供的批处理结果是正确的,当我们做ToList()时,仍然是错误的,因为我们仍然在处理第二种方法中的内存流并且没有分配内存,但仍然不会出现错误的结果, 任何意见/建议

【问题讨论】:

  • 我认为你需要分享你正在执行的代码。
  • 如果您仔细查看问题,代码就在那里,除非您也喜欢从相应链接复制源代码。您认为缺少/不清楚哪一部分,这两种批处理机制都是 IEnumerable 扩展
  • @Veverke 没有批处理工作,这是有趣的部分,正如我提到的,它显示了在执行 ToList() 时的正确结果,但 Count() 不正确。同样不正确的代码是堆栈溢出问题的答案,与 MoreLinq 无关,由于其优化,我更喜欢该答案,但无法指出问题的原因
  • 你有一个伪代码块,你说它“就在那里”。如果您需要帮助,请显示用于创建分区的 C# 代码。
  • 第二个结果返回 Count=10 的原因是因为它使用了 while (enumerator.MoveNext()) ,这将产生 10 次,我假设将返回 7 个额外的空枚举。您希望以什么形式看到这个问题的答案?

标签: c# linq ienumerable morelinq


【解决方案1】:

第二个结果返回 Count=10 的原因是因为它使用了while (enumerator.MoveNext()),这将产生 10 次并导致生成的可枚举包含 10 个可枚举而不是 3。

在引用的问题中以较高的分数https://stackoverflow.com/a/13731854/2138959 回答也为问题提供了合理的解决方案。

【讨论】:

  • 正如在引用的答案中指出的那样,它并不理想,因为它因 GroupBy 而失败了 Lazy 评估
  • 是的,确实如此。
猜你喜欢
  • 1970-01-01
  • 2018-10-03
  • 2020-11-16
  • 1970-01-01
  • 2020-04-13
  • 1970-01-01
  • 1970-01-01
  • 2021-07-04
  • 1970-01-01
相关资源
最近更新 更多