C# の汎用 Map/Reduce リスト拡張機能
-
09-06-2019 - |
質問
私はマップを模倣し、Lisp の関数を減らすためにいくつかの拡張機能を作成しています。
public delegate R ReduceFunction<T,R>(T t, R previous);
public delegate void TransformFunction<T>(T t, params object[] args);
public static R Reduce<T,R>(this List<T> list, ReduceFunction<T,R> r, R initial)
{
var aggregate = initial;
foreach(var t in list)
aggregate = r(t,aggregate);
return aggregate;
}
public static void Transform<T>(this List<T> list, TransformFunction<T> f, params object [] args)
{
foreach(var t in list)
f(t,args);
}
変換関数は次のような煩わしさを軽減します。
foreach(var t in list)
if(conditions && moreconditions)
//do work etc
これには意味がありますか?もっと良くなるでしょうか?
解決
これらは、Linq の拡張機能と非常によく似ています。
//takes a function that matches the Func<T,R> delegate
listInstance.Aggregate(
startingValue,
(x, y) => /* aggregate two subsequent values */ );
//takes a function that matches the Action<T> delegate
listInstance.ForEach(
x => /* do something with x */);
2 番目の例が Transform と呼ばれるのはなぜですか?何らかの方法でリスト内の値を変更するつもりですか?そういう場合は使った方が良いかもしれません ConvertAll<T>
または Select<T>
.
他のヒント
このリンクによると C# 3.0 の関数型プログラミング:マップ/リデュース/フィルターが世界を揺るがす仕組み 以下は、System.Linq 名前空間における C# の同等のものです。
- 地図 --> 列挙可能.選択
- 減らす --> Enumerable.Aggregate
- フィルター --> 列挙可能。どこで
代わりに、組み込みの Func デリゲートを使用します。これと同じコードは、どの IEnumerable でも機能します。コードは次のようになります。
public static R Reduce<T,R>(this IEnumerable<T> list, Func<T,R> r, R initial)
{
var aggregate = initial;
foreach(var t in list)
aggregate = r(t,aggregate);
return aggregate;
}
public static void Transform<T>(this IEnumerable<T> list, Func<T> f)
{
foreach(var t in list)
f(t);
}
渡されたリストを操作するのではなく、マップを実行して新しいリストを返す方法を追加したい場合があります (リストを返すと、他の操作を連鎖させるのに便利です)。おそらく、新しいリストを返すかどうかを示すブール値を含むオーバーロードされたバージョンです。次のようになります。
public static List<T> Transform<T>(this List<T> list, TransformFunction<T> f,
params object [] args)
{
return Transform(list, f, false, args);
}
public static List<T> Transform<T>(this List<T> list, TransformFunction<T> f,
bool create, params object [] args)
{
// Add code to create if create is true (sorry,
// too lazy to actually code this up)
foreach(var t in list)
f(t,args);
return list;
}
内部的に LinQ を使用する拡張メソッドを作成することをお勧めします。 このような:
public static IEnumerable<R> Map<T, R>(this IEnumerable<T> self, Func<T, R> selector) {
return self.Select(selector);
}
public static T Reduce<T>(this IEnumerable<T> self, Func<T, T, T> func) {
return self.Aggregate(func);
}
public static IEnumerable<T> Filter<T>(this IEnumerable<T> self, Func<T, bool> predicate) {
return self.Where(predicate);
}
以下にいくつかの使用例を示します。
IEnumerable<string> myStrings = new List<string>() { "1", "2", "3", "4", "5" };
IEnumerable<int> convertedToInts = myStrings.Map(s => int.Parse(s));
IEnumerable<int> filteredInts = convertedToInts.Filter(i => i <= 3); // Keep 1,2,3
int sumOfAllInts = filteredInts.Reduce((sum, i) => sum + i); // Sum up all ints
Assert.Equal(6, sumOfAllInts); // 1+2+3 is 6
(見る https://github.com/cs-util-com/cscore#ienumerable-extensions 他の例については)
所属していません StackOverflow