假设我有一个integeres数组,我想把它分成几个部分,我想用零作为断开时间的条件。这样的事情:
[1,2,3,0,4,5,0,6,7] => [[1,2,3,0], [4,5,0], [6,7]]
嗯,可以使用两个for循环轻松完成,但我想知道是否可以使用LINQ执行此操作。
有一些问题,例如 [1],[2] ,但与此相反,它们依赖于从列表外部提供的条件。
注意:我知道在一个帖子中提出多个问题是不礼貌的,但是如果有人熟悉函数式编程(因为在本质上,它确实是一个FP问题),我也希望看到他们的观点和这个问题的可能解决方案。
答案 0 :(得分:10)
您的集合中的各个元素之间存在依赖关系,具体而言,对于您想要知道的每个元素,前一个元素为零?"。一旦您的查询依赖于前一个元素(或者更常见的是,只要您的查询依赖于同一序列的其他元素),您就应该达到Aggregate
(或更一般的函数式编程术语,{ {1}})。这是因为fold
与其他LINQ运算符不同,它允许您随身携带状态从一次迭代到下一次迭代。
因此,为了回答您的问题,我在LINQ中编写了如下查询。
Aggregate
我将其分解为部分,以便我能更好地解释我的想法。
// 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;
});
正如我之前所说,因为我们需要携带状态,所以要达到Aggregate。将新的空列表放入列表列表中会删除values.Aggregate(new List<List<int>>{new List<int>()},
的边缘大小写,其中没有列表。
List<List<int>>
再次,查看我们的lambda表达式((list, value) => {...}
)的签名,我们可以看到明确传递的状态:我们接受Func<List<List<int>>, int, List<List<int>>
并返回相同的内容。
List<List<int>>
由于我们总是想要处理最新的list.Last().Add(value);
,我们会得到列表列表的List<int>
元素(由于上面的部分,它们永远不会为null)。
Last()
这是我们进行拆分的地方 - 在下一次迭代中,对Last()的调用将返回这个新列表。
if (value == 0) list.Add(new List<int>());
我们最终将状态传递给下一次迭代。
在return list;
方法中,这可以很容易地推广,如下所示:
SplitOn
由于Enumerables的工作方式,使用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
的版本不太清楚,但同样,并不是特别难以从上面的代码创建,看起来像(通过三元运算符简化了触摸):
List
你也可能会发现Haskell's implementation of splitOn很有趣,因为它正是你想要的。我会称之为不平凡(轻描淡写)。
答案 1 :(得分:1)
这是一个有助于扩展的扩展程序:
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++;
}
}
有了它,下面的技巧
int breakingValue = 0;
var subseq = seq.MarkWithLabels(i => i == breakingValue)
.GroupBy(tup => tup.Item2)
.Select(group => group.Select(tup => tup.Item1).ToArray())
.ToArray();
除了foreach之外,FP解决方案可以基本相同。
答案 2 :(得分:0)
我完全基于Zack's回答编译了两个扩展方法。
[root@radiusx1 ~]# systemctl start radiusd
Warning: radiusd.service changed on disk. Run 'systemctl daemon-reload' to reload units.
Job for radiusd.service failed because the control process exited with error code. See "systemctl status radiusd.service" and "journalctl -xe" for details.
[root@radiusx1 ~]# systemctl status radiusd
● radiusd.service - FreeRADIUS high performance RADIUS server.
Loaded: loaded (/usr/lib/systemd/system/radiusd.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Thu 2017-03-02 17:21:00 PKT; 10s ago
Process: 26712 ExecStartPre=/usr/sbin/radiusd -C (code=exited, status=1/FAILURE)
Process: 26708 ExecStartPre=/bin/chown -R radiusd.radiusd /var/run/radiusd (code=exited, status=0/SUCCESS)
Mar 02 17:20:59 radiusx1.xoultech.com systemd[1]: Starting FreeRADIUS high performance RADIUS server....
Mar 02 17:21:00 radiusx1.xoultech.com systemd[1]: radiusd.service: control process exited, code=exited status=1
Mar 02 17:21:00 radiusx1.xoultech.com systemd[1]: Failed to start FreeRADIUS high performance RADIUS server..
Mar 02 17:21:00 radiusx1.xoultech.com systemd[1]: Unit radiusd.service entered failed state.
Mar 02 17:21:00 radiusx1.xoultech.com systemd[1]: radiusd.service failed.