拆分成列表子列表通过检查元素的条件 [英] Split a list into sublist by checking a condition on elements

查看:138
本文介绍了拆分成列表子列表通过检查元素的条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有integeres的数组,我想将它分割成几个部分,我想用零何时突破的条件。事情是这样的:

Suppose I have an array of integeres and I want to split it into several parts, and I want to use zero as the condition on when to break. Something like this :

[1,2,3,0,4,5,0,6,7] => [[1,2,3,0], [4,5,0], [6,7]]

那么,它可以很容易地利用两个for循环做的,但我想知道是否有可能与LINQ做到这一点。


有一对夫妇这样的问题 [1] ,<一个HREF =htt​​p://stackoverflow.com/questions/10826994/splitting-an-array-using-linq?rq=1> [2] ,但与此相反的,它们依靠一个已经从列表的外侧设置的条件。



注:我知道这不是客气地问多个问题中的一个线程,但如果有任何人都熟悉函数式编程(因为在它的本质上,它是一个真正的FP的问题),我也想看看他们的观点和针对此问题可能的解决方案。

Well, it can be easily done using two for loops, but I wanted to know if it's possible to do this with LINQ.

There's a couple of question like this[1],[2], but as opposed to this one, they rely on a condition that's been provided from outside of the list.

Note: I know it's not polite to ask more than one question in a thread, but if anyone out there is familiar with functional programming ( since in it's essence, it's really an FP question ), I'd also like to see their perspective and possible solutions for this problem.

推荐答案

您有您的收藏分开的元素之间的依赖关系,具体而言,要了解每个元素是previous元素的零?。只要您的查询依赖于previous元素(或者更一般地说,只要您的查询依赖于同一序列的其他元素),你应该达到总结(或更一般功能编程术语,折叠)。这是因为总结,不像其他的LINQ运算符,让你与你从一个迭代携带状态下。

You have a dependency between separate elements of your collection, specifically, for each element you want to know "was the previous element a zero?". As soon as your query depends on the previous element (or, more generally, as soon as your query depends on other elements of the same sequence), you should reach for Aggregate (or in more general functional programming terms, fold). This is because Aggregate, unlike other LINQ operators, allows you to carry state with you from one iteration to the next.

因此​​,要回答你的问题,我想在LINQ如下写这个查询。

So, to answer your question, I'd write this query as follows in LINQ.

// assume our list of integers it called values
var splitByZero = values.Aggregate(new List<List<int>>{new List<int>()},
                                   (list, value) => {
                                       list.Last().Add(value);
                                       if (value == 0) list.Add(new List<int>());
                                       return list;
                                   });

我要打破这种分解成零部件,所以我可以更好地解释我的想法。

I'll break this down into parts so I can better explain my thinking.

values.Aggregate(new List<List<int>>{new List<int>()},

正如我以前说过,达到总结,因为我们需要携带状态。把一个新的空单进入我们的名单列表中删除名单,LT的边缘情况;名单&LT; INT&GT;&GT; 具有它没有列出

(list, value) => {...}

再次看着我们的lambda前pression的签名(即 Func键&LT;名单&LT;名单&LT; INT&GT;&gt;中INT,列表与LT;名单&LT; INT&GT;&GT; ),我们可以看到国家通过明确:我们接受列表&LT;名单&LT; INT&GT;&GT; 并返回相同的

Again, looking at the signature of our lambda expression (which is Func<List<List<int>>, int, List<List<int>>), we can see the state passing explicitly: we accept a List<List<int>> and return the same.

list.Last().Add(value);

由于我们总是希望在最近的列表与LT工作; INT&GT; ,我们得到了最后()我们的名单列表的元素(这永远不会为空,由于上述部分)。

Since we always want to work on the most recent List<int>, we get the Last() element of our list of lists (which will never be null due to the section above).

if (value == 0) list.Add(new List<int>());

这是我们做的分裂 - 对下一个迭代,调用最后一个()将返回这个新的列表

This is where we do the splitting - on the next iteration, the call to Last() will return this new list.

return list;

和我们最终传递给下一次迭代的状态。

And we finally pass the state on to the next iteration.

这可以毫无困难一概而论,在 SplitOn 方法,如下所示:

This could be generalized with little difficulty, in a SplitOn method, as follows:

public static IEnumerable<IEnumerable<T>> SplitOn<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
    return source.Aggregate(new List<List<T>> {new List<T>()},
                            (list, value) =>
                                {
                                    list.Last().Add(value);
                                    if (predicate(value)) list.Add(new List<T>());
                                    return list;
                                });
}

使用的版本的IEnumerable 的而不是列表的是因为这样可枚举工作有点不太清楚,但同样,不是特别难从上面的code创建,看起来像(简体只是通过三元运算符触摸):

The version using IEnumerable's instead of List's is somewhat less clear because of the way Enumerables work, but again, isn't particularly hard to create from the above code, and looks like (simplified just a touch via a ternary operator):

public static IEnumerable<IEnumerable<T>> SplitOn<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
    return source.Aggregate(Enumerable.Repeat(Enumerable.Empty<T>(), 1),
                            (list, value) =>
                                {
                                    list.Last().Concat(Enumerable.Repeat(value, 1));
                                    return predicate(value) ? list.Concat(Enumerable.Repeat(Enumerable.Empty<T>(), 1)) : list;
                                });
}


您也可能会发现<一个href=\"http://hackage.haskell.org/package/split-0.1.1/docs/src/Data-List-Split-Internals.html#splitOn\">Haskell's实施splitOn的的有趣,因为它正是你想要的。我把它叫做平凡(把它轻轻)。


You also might find Haskell's implementation of splitOn interesting, as it does exactly what you want. I would call it nontrivial (to put it lightly).

这篇关于拆分成列表子列表通过检查元素的条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆