C# 有没有更好的方法来获取每个项都与谓词匹配的子序列?

C# 有没有更好的方法来获取每个项都与谓词匹配的子序列?,c#,linq,C#,Linq,假设我有一个数字。例如,{2,1,42,0,9,6,5,3,8} 我需要得到匹配谓词的项的“运行”。例如,如果我的谓词是 bool isSmallerThanSix(int number){...} 我希望获得以下输出: {{2,1},{0},{5,3} 是否有一个内置函数可以实现这一点 到目前为止,我有: public static IEnumerable<IEnumerable<T>> GetSequences<T>(this IEnumerable&l

假设我有一个数字。例如,{2,1,42,0,9,6,5,3,8}

我需要得到匹配谓词的项的“运行”。例如,如果我的谓词是

bool isSmallerThanSix(int number){...}
我希望获得以下输出: {{2,1},{0},{5,3}

是否有一个内置函数可以实现这一点

到目前为止,我有:

public static IEnumerable<IEnumerable<T>> GetSequences<T>(this IEnumerable<T> source,
      Func<T, bool> selector) {

        if (source == null || selector == null) {
            yield break;
        }

        IEnumerable<T> rest = source.SkipWhile(obj => !selector(obj));

        while (rest.Count() > 0) {
            yield return rest.TakeWhile(obj => selector(obj));
            rest = rest
                    .SkipWhile(obj => selector(obj))
                    .SkipWhile(obj => !selector(obj));
        }


    }
公共静态IEnumerable GetSequences(此IEnumerable源,
Func选择器){
如果(源==null | |选择器==null){
屈服断裂;
}
IEnumerable rest=source.SkipWhile(obj=>!selector(obj));
while(rest.Count()>0){
收益率返回rest.TakeWhile(obj=>selector(obj));
休息=休息
.SkipWhile(obj=>选择器(obj))
.SkipWhile(obj=>!选择器(obj));
}
}
这似乎是可行的,但我是在半夜写的,从星期二起,效率低达十五种。是否有更好的、最好是内置的(因此经过良好测试的)方法

非常感谢你们抽出时间


Ria。

据我所知,没有内置方法。但是,对
IEnumerable
调用
Count
扩展方法不是很有效,因为它必须枚举列表才能获得计数。因此,我提出了这个具有相同效果的方法

public static IEnumerable<IEnumerable<T>> 
    GetSequences<T>(this IEnumerable<T> source, Func<T, bool> selector)
{
    // omitted null checks for brevity
    var list = new List<T>();

    foreach(var item in source)
    {
        if (selector.Invoke(item))
        {
            list.Add(item);
        }
        else if (list.Count > 0)
        {
            yield return list;
            list = new List<T>();
        }
    }

    if (list.Count > 0)
        yield return list;
}
公共静态IEnumerable
GetSequences(此IEnumerable源,Func选择器)
{
//为简洁起见省略了空检查
var list=新列表();
foreach(源中的var项)
{
if(选择器调用(项))
{
列表。添加(项目);
}
否则如果(list.Count>0)
{
收益回报表;
列表=新列表();
}
}
如果(list.Count>0)
收益回报表;
}

正如Jon Skeet所提到的,在这种情况下,
SkipWhile
TakeWhile
的使用似乎效率很低,因为它们会一个迭代器一个迭代器地创建迭代器。您可以在调试示例时检查这一点,因为当您一步一步地尝试查找下一个序列时,它会变得有点疯狂,等等,即使示例很简单。

我怀疑您的代码在所有情况下都不起作用。特别是,SkipWhile和TakeWhile是惰性计算的-如果调用代码没有实际读取所有生成的
IEnumerables
(或者更糟的是,缓冲它们并以不同的顺序读取它们!),我强烈怀疑您会得到错误的结果

我想你真的需要做一些事情,比如:

public static IEnumerable<IEnumerable<T>> GetSequences<T>(
    this IEnumerable<T> source,
    Func<T, bool> selector)
{
    List<T> current = new List<T>();
    foreach (T element in source)
    {
        if (selector(element))
        {
            current.Add(element);
        }
        else if (current.Count > 0)
        {
            yield return current;
            current = new List<T>();
        }           
    }
    if (current.Count > 0)
    {
        yield return current;
    }
}
公共静态IEnumerable GetSequences(
这是一个数不清的来源,
Func选择器)
{
列表当前=新列表();
foreach(源中的T元素)
{
if(选择器(元件))
{
当前。添加(元素);
}
否则如果(当前计数>0)
{
产生回流电流;
当前=新列表();
}           
}
如果(当前计数>0)
{
产生回流电流;
}
}
(这忽略了错误检查-由于迭代器块的延迟执行,您可能希望在单独的方法中执行此操作,然后将此方法作为私有方法调用-这是编写生产质量迭代器块时非常常见的模式。)


List
的选择有些随意,顺便说一句-例如,您当然可以使用
LinkedList
。或者,如果您选择列表,您可以返回
IEnumerable
而不是
IEnumerable
,这可能会使呼叫者更容易处理结果。

我不确定我在说什么,但这不会阻止我说话

定义一种新类型的迭代器,在当前子序列完成时可以跳到下一个子序列,这有意义吗

使用Java和Integer示例语法,客户端代码如下所示:

Iterator2<Integer> iter = getSequencences(someCollection, someCriteria)
while (iter.hasNextSequence())
{
  iter.nextSequence();
  while (iter.hasNext())
  {
    Integer i = iter.next();
  }
}
iterator2iter=getSequences(someCollection,someCriteria)
while(iter.hasNextSequence())
{
iter.nextSequence();
while(iter.hasNext())
{
整数i=iter.next();
}
}

*通过直接使用枚举器,可以生成完全延迟的结果

我可能会使用Garry和Jon提供的版本,因为它更简单,并且可以完成任务,但是写起来很有趣

public static class Program
{
    static IEnumerable<IEnumerable<T>>
        GetSequences<T>(this IEnumerable<T> source, Func<T, bool> selector)
    {
        var enumerator = source.GetEnumerator();
        while (enumerator.MoveNext())
            if (selector(enumerator.Current))
                yield return TakeWhile(enumerator, selector);
        yield break;
    }

    static IEnumerable<T> 
        TakeWhile<T>(IEnumerator<T> enumerator, Func<T, bool> selector)
    {
        do
        {
            yield return enumerator.Current;
        } while (enumerator.MoveNext() && selector(enumerator.Current));
        yield break;
    }

    static void Main()
    {
        var nums = new[] { 2, 1, 42, 0, 9, 6, 5, 3, 8 };
        var seqs = nums.GetSequences(i => i < 6);

        Console.WriteLine(string.Join(",", seqs.Select(s =>
            string.Format("{{{0}}}",
                string.Join(",", s.Select(i => i.ToString()).ToArray())
            )).ToArray()));
    }
}
公共静态类程序
{
静态可数
GetSequences(此IEnumerable源,Func选择器)
{
var枚举器=source.GetEnumerator();
while(枚举数.MoveNext())
if(选择器(枚举器当前))
生成返回TakeWhile(枚举器、选择器);
屈服断裂;
}
静态可数
TakeWhile(IEnumerator枚举器、Func选择器)
{
做
{
产生返回枚举数。当前;
}while(enumerator.MoveNext()&&selector(enumerator.Current));
屈服断裂;
}
静态void Main()
{
var nums=new[]{2,1,42,0,9,6,5,3,8};
var-seqs=nums.GetSequences(i=>i<6);
Console.WriteLine(string.Join(“,”,seqs.Select(s=>
string.Format(“{{0}}}”,
string.Join(“,”s.Select(i=>i.ToString()).ToArray()
)).ToArray());
}
}

您可以利用GroupBy将按顺序运行项目这一事实

    public static IEnumerable<IEnumerable<T>> GetSequences<T>
      (this IEnumerable<T> source, Func<T, bool> predicate)
    {
        bool flag = false;
        int id = 0;
        return source.GroupBy(x =>
            {
                bool match = predicate(x);
                if (match != flag)
                {
                    id += 1;
                    flag = match;
                }
                return new { keep = match, id = id };
            })
            .Where(g => g.Key.keep)
            .Select(g => g.AsEnumerable());
    }
公共静态IEnumerable GetSequences
(此IEnumerable源,Func谓词)
{
布尔标志=假;
int id=0;
返回source.GroupBy(x=>
{
布尔匹配=谓词(x);
如果(匹配!=标志)
{
id+=1;
旗子=比赛;
}
返回新的{keep=match,id=id};
})
.Where(g=>g.Key.keep)
.Select(g=>g.AsEnumerable());
}
    public static void Test1()
    {
        List<int> myList = new List<int>() {2,1,42,0,9,6,5,3,8};

        foreach (var g in myList.GetSequences(i => i < 6))
        {
            Console.WriteLine("g");
            foreach (int i in g)
            {
                Console.WriteLine(i);
            }
        }
    }