Общая карта / Сокращение расширений списка в C # - PullRequest
26 голосов
/ 02 сентября 2008

Я пишу несколько расширений для имитации карты и сокращения функций в Лиспе.

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

Имеет ли это смысл? Может ли быть лучше?

Ответы [ 5 ]

50 голосов
/ 10 ноября 2008

По этой ссылке Функциональное программирование в C # 3.0: как Map / Reduce / Filter может потрясти ваш мир следующие значения в C # в пространстве имен System.Linq следующие:

35 голосов
/ 02 сентября 2008

Они очень похожи на расширения в 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 */);

Почему второй пример называется Transform? Вы намерены как-то изменить значения в списке? Если это так, вам лучше использовать ConvertAll<T> или Select<T>.

2 голосов
/ 02 сентября 2008

Я бы вместо этого использовал встроенные делегаты 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);
}
1 голос
/ 21 февраля 2019

Я бы порекомендовал создать методы расширения, которые внутренне используют 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 для дополнительных примеров)

1 голос
/ 02 сентября 2008

Возможно, вы захотите добавить способ создания карты, но вернуть новый список, вместо того, чтобы работать с переданным списком (и возвращение списка может оказаться полезным для объединения других операций) ... возможно, перегруженная версия с логическое значение, которое указывает, хотите ли вы вернуть новый список или нет, как таковой:

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;
}
...