我经常发现自己想在IEnumerables上使用Head和Tail方法,这在Linq中并不存在。虽然我可以很容易地写我自己的,但我想知道他们是否被故意遗漏了。比如说,
var finalCondition = new Sql("WHERE @0 = @1", conditions.Head().Key, conditions.Head().Value);
foreach (var condition in conditions.Tail())
{
finalCondition.Append("AND @0 = @1", condition.Key, condition.Value);
}
那么,使用Linq的最佳实践是什么呢?我一直在寻找它的用途,这是否表明我没有做一些推荐的事情?如果不是,那么为什么在Linq中没有实现这种常见的函数范式呢?
4条答案
按热度按时间xeufq47z1#
从技术上讲,你的头是.First(),你的尾是.Skip(1)。但也许你能找到更好的解决办法?比如在IEnumerable上使用.Aggregate()?
b1zrtrql2#
鉴于
IEnumerable<T>
的接口,性能并不总是能够得到保证。您注意到大多数函数式编程语言都实现了tail和head。然而,应该注意的是,这些语言是在记忆结构中起作用的。
IEnumerable<T>
没有任何这样的约束,因此不能假定这是有效的。例如,一个常见的函数模式是递归地处理集合的头部,然后递归地处理调用的尾部。
例如,如果您使用Entity Framework执行此操作,您将向SQL Server发送以下(Meta)调用,紧密循环。
这将是非常低效的。
编辑:
仔细想想。另一个原因是C#/VB.NET不支持尾递归,因此,这种模式很容易导致
StackOverflow
。hc2pp10m3#
因为“Head & Tail”概念在pattern matching和递归调用的函数式编程中使用。由于C#不支持模式匹配,因此不需要实现head()和tail()方法。
至于你的情况-你应该使用Aggregate方法。
km0tfn4u4#
我并不是建议在生产代码中使用这种方法,但我试图向一些了解C#的朋友解释Haskell代码片段,并发现我可以通过混合
IEnumerable<T>
和IEnumerator<T>
来实现这一点(你可以,因为IEnumerable
总是给你GetEnumerator()
,你可以使用迭代器(yield return
)将IEnumerator
转换为IEnumerable
。迭代器为您提供了模拟惰性的好处(在某些上下文中,如果使用正确)。(to纯粹主义者-在原始上下文中,这两个都是局部函数,因此它们以小写字母开头)
我怀疑有一种方法可以做到这一点,而不(显式地)引入
IEnumerator
(可能仍然涉及迭代器),但我还没有偶然发现它。(在今天闲逛时,我刚刚发现
IEnumerator<T>
实现了IDisposable
,而IEnumerator
没有。Geez,the stuff ya never notice(天啊,你从来没有注意到的东西)