Как получить список промежуточных сумм функциональным путем? Используя LINQ?

У вас может быть что-то подобное

    enum C1 {
        WITH_PARAM("value"),
        EMPTY();

        private String value;
        C1(String s) {
            System.out.println("with param = " +s);
            value=s;
        }
        C1() {
            System.out.println("without param");
        }
        public void g() {
            System.out.println("inside g, value is "+value);
        }
    }

        public static void main(String s[]) {
            C1.EMPTY.g();
            C1.WITH_PARAM.g();

        }
.
6
задан ShuggyCoUk 21 April 2009 в 21:39
поделиться

7 ответов

в функциональном смысле это комбинация:

zip

взять две последовательности и создать последовательность кортежей элементов

и

map

Take функция f и последовательность и возвращают новую последовательность, которая является f (x) для каждого x в исходной последовательности

Индекс тривиален в c # 4.0 Если взять упрощенную реализацию, у нас есть

static class Enumerable 
{ 
    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
        this IEnumerable<TFirst> first, 
        IEnumerable<TSecond> second, 
        Func<TFirst, TSecond, TResult> func) 
    { 
        var ie1 = first.GetEnumerator(); 
        var ie2 = second.GetEnumerator();

        while (ie1.MoveNext() && ie2.MoveNext()) 
            yield return func(ie1.Current, ie2.Current); 
    } 
}

. Затем нам нужна карта. У нас это уже есть, это то, что мы называем Select в c #

IEnumerable<int> input = { 1,2,3,4 };
int a = 0;
var accumulate = input.Select(x => 
    {
         a += x; 
         return a;
    });

Но безопаснее запекать это в свой собственный метод (без каррирования в c #) и разрешать поддержку произвольных типов / накоплений.

static class Enumerable 
{ 
    public static IEnumerable<T> SelectAccumulate<T>(
        this IEnumerable<T> seq,
        Func<T,T,T> accumulator) 
    { 
        var e = seq.GetEnumerator(); 
        T t = default(T);             
        while (e.MoveNext()) 
        {
            t = accumulator(t, e.Current);
            yield return t;
        } 
    } 
}

Затем мы можем собрать их вместе например,

var input = new int[] {1,2,3};
var mapsum = input.Zip(
    input.SelectAccumulate((x,y) => x+y), 
    (a,b) => new {a,b});

Это будет повторять последовательность дважды, но это более общее. Вы можете сделать аккумулятор самостоятельно в рамках стандартного выбора и простого замыкания, но он больше не так полезен, как «строительный блок», являющийся одной из движущих сил функционального программирования.

Поддержка кортежей - это боль, кроме как внутри метод как анонимные типы не пересекает границы метода без особых хлопот. Несколько основных кортежей должны быть включены в c # 4.0. предполагая, что класс / структура кортежа называется Pair

6
ответ дан 8 December 2019 в 13:49
поделиться

Я думаю, что это самый короткий подход:

int sum = 0;
var result = input.Select(i => new { i, S = sum += i });
6
ответ дан 8 December 2019 в 13:49
поделиться
    int runningTotal=0;
    var p = input.Select((l)=>
        {
            runningTotal+=l;
            return new {l,Total=runningTotal};
        });

Редактировать

Foreach всегда будет в порядке. Откройте отражатель и посмотрите на ForEach в списке (ForEach не существует в массиве), но все, что делает ForEach - это цикл for для элементов.

Я не уверен насчет select, насколько я знаю но я никогда не копался в этом.

2
ответ дан 8 December 2019 в 13:49
поделиться
var input = new int[] {1,2,3}
var output = new List<KeyValuePair<int,int>>();
int runningTotal = 0;

foreach (int current in input)
{
  runningTotal += current;
  output.Add(new KeyValuePair(current, runningTotal);
}

Было бы легко преобразовать это в функцию Linq .Foreach (), если вы действительно этого хотите. проблема будет в том, если вы не хотите иметь отдельный промежуточный итог.

функциональная версия:

intput.Foreach(current=>
{
    runningTotal += current;
      output.Add(new KeyValuePair(current, runningTotal);

}
);
1
ответ дан 8 December 2019 в 13:49
поделиться
var output = input.Select((i, indexI) => 
    new {
           Element = i,
           RunningSum = input.Where((j, indexJ) => indexJ <= indexI).Sum()
        });

Это будет получить коллекцию анонимного типа с двумя свойствами Element и RunningSum.

UPDTAE

Вот еще одно решение, использующее только метод расширения LINQ Aggregate. И да, это ужасно.

var output = input.Aggregate(
   new List<KeyValuePair<Int32, Int32>>(),
   (result, element) =>
   {
      result.Add(new KeyValuePair<Int32, Int32>(
         element,
         element + result.LastOrDefault().Value));
      return result;
   });
2
ответ дан 8 December 2019 в 13:49
поделиться

Functional implementation (no side effects) and no extensions:

internal struct Tuple<T> {
    public T A;
    public T B;
}

internal class Program {
    private static void Main(string[] args) {
        var ints = new[] { 5, 7, 11, 13 };
        IEnumerable<Tuple<int>> result = ints.Aggregate<int, IEnumerable<Tuple<int>>>(new List<Tuple<int>>(),
                (sum, item) => sum.Concat(new[] { new Tuple<int> { A = sum.LastOrDefault().A + item, B = item } }));

        Console.WriteLine(string.Join(" ", result.Select(item => "(" + item.A + "," + item.B + ")").ToArray()));
    }
}

Though can't figure out how to make it with anonymous type instead of Tuple. Также может быть упрощено, если будет:

IEnumerable<T> Append(this IEnumerable<T> @this, T item);
0
ответ дан 8 December 2019 в 13:49
поделиться

Просто для забавы, здесь это в F # с использованием встроенной функции Seq.scan:

> let input = [1;2;3];;

val input : int list

> input |> Seq.scan (fun (_, acc) x -> (x, acc + x)) (0,0) |> Seq.skip 1;;

val it : seq<int * int> = seq [(1, 1); (2, 3); (3, 6)]
0
ответ дан 8 December 2019 в 13:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: