C# 通过检查元素上的条件将列表拆分为子列表
假设我有一个整数数组,我想把它分成几个部分,我想用零作为何时中断的条件。类似这样的内容:C# 通过检查元素上的条件将列表拆分为子列表,c#,arrays,linq,list,functional-programming,C#,Arrays,Linq,List,Functional Programming,假设我有一个整数数组,我想把它分成几个部分,我想用零作为何时中断的条件。类似这样的内容: [1,2,3,0,4,5,0,6,7] => [[1,2,3,0], [4,5,0], [6,7]] 嗯,使用两个for循环可以很容易地完成,但我想知道是否可以使用LINQ来完成这项工作。 有几个问题是这样的,但与此相反,它们依赖于列表之外提供的条件。 注意:我知道在一个线程中问一个以上的问题是不礼貌的,但是如果有人熟悉函数式编程(因为本质上,这确实是一个FP问题),我也希望看到他们的观点和可能的
[1,2,3,0,4,5,0,6,7] => [[1,2,3,0], [4,5,0], [6,7]]
嗯,使用两个for循环可以很容易地完成,但我想知道是否可以使用LINQ来完成这项工作。有几个问题是这样的,但与此相反,它们依赖于列表之外提供的条件。
注意:我知道在一个线程中问一个以上的问题是不礼貌的,但是如果有人熟悉函数式编程(因为本质上,这确实是一个FP问题),我也希望看到他们的观点和可能的解决方案。这里有一个扩展,可以帮助:
public static IEnumerable<Tuple<TIn, int>> MarkWithLabels<TIn>(this IEnumerable<TIn> src, Predicate<TIn> splittingCondition)
{
int label = 0;
foreach (TIn item in src)
{
yield return new Tuple<TIn, int>(item, label);
if (splittingCondition(item))
label++;
}
}
FP解决方案可能基本相同,但foreach除外。您的集合中的不同元素之间存在依赖关系,具体来说,对于您想知道的每个元素,“前一个元素是零吗?”。只要您的查询依赖于上一个元素(或者更一般地说,只要您的查询依赖于相同序列的其他元素),您就应该使用
Aggregate
(或者在更一般的函数编程术语中,fold
)。这是因为与其他LINQ运算符不同,Aggregate
,允许您将状态从一个迭代带到下一个迭代
因此,为了回答您的问题,我将在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;
});
再次,查看lambda表达式的签名(即
Func),我编译了两个完全基于答案的扩展方法
公共静态IEnumerable SplitBefore(此IEnumerable源,Func谓词)
{
返回源。聚合(
可枚举。重复(新列表(),1),
(列表,值)=>
{
if(谓词(值))
list=list.Concat(Enumerable.Repeat(newlist(),1));
list.Last().Add(值);
退货清单;
}
)
.Where(list=>list.Any());
}
公共静态IEnumerable SplitAfter(此IEnumerable源,Func谓词)
{
返回源。聚合(
可枚举。重复(新列表(),1),
(列表,值)=>
{
list.Last().Add(值);
返回谓词(值)
?list.Concat(可枚举。重复(新列表(),1))
:列表;
}
)
.Where(list=>list.Any());
}
不幸的是,IEnumerable
版本不起作用,因为.Concat()
不会像那样改变调用对象。Add()
会改变调用对象,因为该行没有返回什么都不做,结果总是空的。很多可枚举的空。为了解决这个问题,同时又不使事情太复杂,我决定构建IEnumerable
,使用.Add()
作为值,使用.Concat()
作为拆分列表。谢谢你的回答,这是最有洞察力的。
// 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;
});
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;
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;
});
}
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;
});
}
public static IEnumerable<List<T>> SplitBefore<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
return source.Aggregate(
Enumerable.Repeat(new List<T>(), 1),
(list, value) =>
{
if (predicate(value))
list = list.Concat(Enumerable.Repeat(new List<T>(), 1));
list.Last().Add(value);
return list;
}
)
.Where(list => list.Any());
}
public static IEnumerable<List<T>> SplitAfter<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
return source.Aggregate(
Enumerable.Repeat(new List<T>(), 1),
(list, value) =>
{
list.Last().Add(value);
return predicate(value)
? list.Concat(Enumerable.Repeat(new List<T>(), 1))
: list;
}
)
.Where(list => list.Any());
}