K组合器(C#,Scala)
我经常在Ruby中使用K组合器,当通过副作用而不是返回值执行折叠操作时,大多数情况下是在折叠中使用,例如以下示例:
some_collection.reduce(Hash.new(0)) {|acc, el| acc[el] += 1 }
这将计算每个元素在中出现的频率some_collection
。不幸的是,它实际上并不起作用,因为该块必须在每次迭代时返回累加器的新值,但是在Ruby中,赋值运算为赋值。
因此,您必须像这样精确地返回累加器的新值:
some_collection.reduce(Hash.new(0)) {|acc, el| acc[el] += 1; acc }
但是我发现这种显式的排序很丑陋,使用折叠功能。使用K组合器(Object#tap
在Ruby中称为):
some_collection.reduce(Hash.new(0)) {|acc, el| acc.tap { acc[el] += 1 }}
我已经在C#中错过了几次(主要是由于某种原因,集合变量(例如List.Add
return void
而不是this
)和Scala),所以我解决了这个问题:
namespace GenericExtensions
{
public static class GenericExtensions
{
public static T Tap<T>(this T o, Action<T> f)
{
Contract.Requires(o != null);
Contract.Requires(f != null);
f(o);
return o;
}
public static T Tap<T>(this T o, Action f)
{
Contract.Requires(o != null);
Contract.Requires(f != null);
f();
return o;
}
}
}
在Scala中:
class Tap[T](o: T) {
def tap(f: T => Unit) = { f(o); o }
def tap(f: => Unit) = { f; o }
}
object Implicits { implicit def any2Tap[T](o: T) = new Tap(o) }
身份功能(Ruby)
我在Ruby中缺少的东西是一种访问身份函数的好方法。Haskell在名称下提供身份功能id
,Scala 名称下提供Scala identity
。这样一来,您可以编写如下代码:
someCollection.groupBy(identity)
Ruby中的等效项是
some_collection.group_by {|x| x }
不会完全从舌头上滑下来吗?
解决方法是
IDENTITY = -> x { x }
some_collection.group_by(&IDENTITY)
ForEach(.NET)
C#中另一个非常缺失的方法:
namespace IEnumerableExtensions
{
public static class IEnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> xs, Action<T> f)
{
Contract.Requires(xs != null);
Contract.Requires(f != null);
foreach (var x in xs) f(x);
}
}
}