5

我想将 C# 中的序列拆分为使用 LINQ 的序列序列。我做了一些调查,我发现的最接近的 SO 文章稍微相关的是this

但是,这个问题只问如何根据常数值对原始序列进行分区。我想根据操作对我的序列进行分区。

具体来说,我有一个包含小数属性的对象列表。

public class ExampleClass
{
    public decimal TheValue { get; set; }
}

假设我有一个 的序列ExampleClass,对应的值序列TheValue是:

{0,1,2,3,1,1,4,6,7,0,1,0,2,3,5,7,6,5,4,3,2,1}

我想将原始序列划分为一个类似的IEnumerable<IEnumerable<ExampleClass>>TheValue

{{0,1,2,3}, {1,1,4,6,7}, {0,1}, {0,2,3,5,7}, {6,5,4,3,2,1}}

我只是不知道如何实施。所以,你能帮忙吗?

我现在有一个非常丑陋的解决方案,但有一种“感觉”,即 LINQ 会增加我的代码的优雅度。

4

3 回答 3

6

好吧,我想我们可以做到这一点...

public static IEnumerable<IEnumerable<TElement>>
    PartitionMontonically<TElement, TKey>
    (this IEnumerable<TElement> source,
     Func<TElement, TKey> selector)
{
    // TODO: Argument validation and custom comparisons
    Comparer<TKey> keyComparer = Comparer<TKey>.Default;

    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            yield break;
        }
        TKey currentKey = selector(iterator.Current);
        List<TElement> currentList = new List<TElement> { iterator.Current };
        int sign = 0;
        while (iterator.MoveNext())
        {
            TElement element = iterator.Current;
            TKey key = selector(element);
            int nextSign = Math.Sign(keyComparer.Compare(currentKey, key));

            // Haven't decided a direction yet
            if (sign == 0)
            {
                sign = nextSign;
                currentList.Add(element);
            }
            // Same direction or no change
            else if (sign == nextSign || nextSign == 0)
            {
                currentList.Add(element);
            }
            else // Change in direction: yield current list and start a new one
            {
                yield return currentList;
                currentList = new List<TElement> { element };
                sign = 0;
            }
            currentKey = key;
        }
        yield return currentList;
    }
}

完全未经测试,但我认为它可能会工作......

于 2011-07-11T17:01:45.617 回答
1

或者使用 linq 运算符和一些滥用 .net 闭包的引用。

public static IEnumerable<IEnumerable<T>> Monotonic<T>(this IEnumerable<T> enumerable)
{
  var comparator = Comparer<T>.Default;
  int i = 0;
  T last = default(T);
  return enumerable.GroupBy((value) => { i = comparator.Compare(value, last) > 0 ? i : i+1; last = value; return i; }).Select((group) => group.Select((_) => _));
}

取自一些用于将 IEnumerable 分区为临时表以进行日志记录的随机实用程序代码。如果我没记错的话,奇怪的结尾 Select 是为了防止输入是字符串枚举时出现歧义。

于 2011-07-11T20:03:14.103 回答
1

这是一个自定义 LINQ 运算符,它根据几乎任何标准拆分序列。它的参数是:

  1. xs:输入元素序列。
  2. func:一个接受“当前”输入元素和状态对象的函数,并作为元组返回:
    • 说明输入bool序列是否应在“当前”元素之前拆分;和
    • 一个状态对象,它将被传递给func.
  3. initialStatefunc:第一次调用时传递给的状态对象。

在这里,还有一个辅助类(必需的,因为yield return显然不能嵌套):

public static IEnumerable<IEnumerable<T>> Split<T, TState>(
                  this IEnumerable<T> xs,
                  Func<T, TState, Tuple<bool, TState>> func, 
                  TState initialState)
{
    using (var splitter = new Splitter<T, TState>(xs, func, initialState))
    {
        while (splitter.HasNext)
        {
            yield return splitter.GetNext();
        }
    }
}
internal sealed class Splitter<T, TState> : IDisposable
{
    public Splitter(IEnumerable<T> xs, 
                    Func<T, TState, Tuple<bool, TState>> func, 
                    TState initialState)
    {
        this.xs = xs.GetEnumerator();
        this.func = func;
        this.state = initialState;
        this.hasNext = this.xs.MoveNext();
    }

    private readonly IEnumerator<T> xs;
    private readonly Func<T, TState, Tuple<bool, TState>> func;
    private bool hasNext;
    private TState state;

    public bool HasNext { get { return hasNext; } }

    public IEnumerable<T> GetNext()
    {
        while (hasNext)
        {
            Tuple<bool, TState> decision = func(xs.Current, state);
            state = decision.Item2;
            if (decision.Item1) yield break;
            yield return xs.Current;
            hasNext = xs.MoveNext();
        }
    }

    public void Dispose() { xs.Dispose(); }
}

注意:以下是该方法中的一些设计决策Split

  • 它应该只对序列进行一次传递。
  • 状态是明确的,因此可以避免副作用func
于 2013-01-23T22:46:49.057 回答