K Combinator(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 などのコレクションミューテーター)とScalaでそれを数回見逃しましたので、私はこれを持ち運びます:void
this
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に欠けているものは、ID関数にアクセスするための適切な名前の付いた方法です。Haskellはid
、Scala という名前で識別関数を提供しますidentity
。これにより、次のようなコードを記述できます。
someCollection.groupBy(identity)
Rubyの同等のものは
some_collection.group_by {|x| x }
舌から正確に転がり落ちませんか?
修正は
IDENTITY = -> x { x }
some_collection.group_by(&IDENTITY)
ForEach(.NET)
C#のもう1つの非常に欠落しているメソッド:
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);
}
}
}