Combinateur K (C #, Scala)
J'utilise le combinateur K dans Ruby assez souvent, principalement dans les plis lorsque l'opération de pliage est effectuée via un effet secondaire plutôt qu'une valeur de retour, comme dans cet exemple:
some_collection.reduce(Hash.new(0)) {|acc, el| acc[el] += 1 }
Cela compte la fréquence à laquelle chaque élément se produit some_collection
. Malheureusement, cela ne fonctionne pas réellement, car le bloc doit renvoyer la nouvelle valeur de l'accumulateur à chaque itération, mais dans Ruby, les affectations sont évaluées selon la valeur affectée.
Donc, vous devez renvoyer explicitement la nouvelle valeur de l'accumulateur comme ceci:
some_collection.reduce(Hash.new(0)) {|acc, el| acc[el] += 1; acc }
Mais je trouve ce séquencement explicite laid dans ce style fonctionnel utilisant des plis. Le combinateur K (appelé Object#tap
en Ruby) à la rescousse:
some_collection.reduce(Hash.new(0)) {|acc, el| acc.tap { acc[el] += 1 }}
Je l'ai déjà manqué plusieurs fois en C # (principalement parce que pour une raison quelconque, les mutateurs de collection tels que List.Add
return void
au lieu de this
) et Scala, je porte donc ceci:
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;
}
}
}
et à 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) }
Fonction d'identité (Ruby)
Quelque chose qui me manque dans Ruby, est un moyen bien nommé d'accéder à la fonction d'identité. Haskell fournit la fonction d'identité sous le nom de id
, Scala sous le nom de identity
. Cela permet d'écrire du code comme:
someCollection.groupBy(identity)
L'équivalent en Ruby est
some_collection.group_by {|x| x }
Ne déroule pas exactement la langue, n'est-ce pas?
Le correctif est
IDENTITY = -> x { x }
some_collection.group_by(&IDENTITY)
ForEach (.NET)
Une autre méthode cruellement manquante en 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);
}
}
}