Rsh*_*Rsh 5 c# linq arrays functional-programming list
假设我有一个integeres数组,我想将它分成几个部分,我想用零作为何时破坏的条件.像这样的东西:
[1,2,3,0,4,5,0,6,7] => [[1,2,3,0], [4,5,0], [6,7]]
Run Code Online (Sandbox Code Playgroud)
好吧,可以使用两个for循环轻松完成,但我想知道是否可以使用LINQ执行此操作.
有这样的问题[1],[2],但与此相反,它们依赖于从列表外部提供的条件.
注意:我知道在一个帖子中提出多个问题是不礼貌的,但是如果有人熟悉函数式编程(因为在本质上,它确实是一个FP问题),我也希望看到他们的观点和这个问题的可能解决方案.
Zac*_*her 12
您在集合的单独元素之间存在依赖关系,特别是对于您想要知道的每个元素"前一个元素是零吗?".一旦您的查询依赖于前一个元素(或者Aggregate
更常见的是,只要您的查询依赖于同一序列的其他元素),您就应该(或者更常见的函数编程术语fold
).这是因为Aggregate
,与其他LINQ运算符不同,它允许您随身携带状态从一次迭代到下一次迭代.
那么,为了回答你的问题,我将在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;
});
Run Code Online (Sandbox Code Playgroud)
我将其分解为部分,以便我能更好地解释我的想法.
values.Aggregate(new List<List<int>>{new List<int>()},
Run Code Online (Sandbox Code Playgroud)
正如我之前所说,因为我们需要携带状态,所以要达到Aggregate.将新的空列表放入列表列表中会删除List<List<int>>
其中没有列表的边缘情况.
(list, value) => {...}
Run Code Online (Sandbox Code Playgroud)
再一次,查看我们的lambda表达式的签名(即Func<List<List<int>>, int, List<List<int>>
),我们可以看到明确传递的状态:我们接受List<List<int>>
并返回相同的状态.
list.Last().Add(value);
Run Code Online (Sandbox Code Playgroud)
由于我们总是想要处理最新的List<int>
,我们得到Last()
列表列表的元素(由于上面的部分,它将永远不会为null).
if (value == 0) list.Add(new List<int>());
Run Code Online (Sandbox Code Playgroud)
这是我们进行拆分的地方 - 在下一次迭代中,对Last()的调用将返回这个新列表.
return list;
Run Code Online (Sandbox Code Playgroud)
我们最终将状态传递给下一次迭代.
在SplitOn
方法中,这可以很容易地推广如下:
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;
});
}
Run Code Online (Sandbox Code Playgroud)
由于Enumerables的工作方式,使用IEnumerable
's代替List
's 的版本有点不太清楚,但同样,从上面的代码创建并不是特别难,看起来像(通过三元运算符简化了触摸):
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;
});
}
Run Code Online (Sandbox Code Playgroud)
您也可能会发现Haskell对splitOn的实现很有趣,因为它完全符合您的要求.我会称之为不平凡(轻描淡写).
归档时间: |
|
查看次数: |
3375 次 |
最近记录: |