tutorial sintaxis query espaƱol c# .net linq

c# - sintaxis - Se explica el algoritmo agregado de LINQ



sintaxis linq c# (11)

Una imagen vale mas que mil palabras

Recordatorio: Func<A, B, C> es una función con dos entradas de tipo A y B , que devuelve una C

Enumerable.Aggregate tiene tres sobrecargas:


Sobrecarga 1:

A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)

Ejemplo:

new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10


Esta sobrecarga es simple, pero tiene las siguientes limitaciones:

  • la secuencia debe contener al menos un elemento,
    de lo contrario, la función lanzará una InvalidOperationException .
  • Los elementos y el resultado deben ser del mismo tipo.


Sobrecarga 2:

B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)

Ejemplo:

var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"}; var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2


Esta sobrecarga es más general:

  • debe proporcionarse un valor semilla ( bIn ).
  • la colección puede estar vacía,
    en este caso, la función producirá el valor semilla como resultado.
  • Los elementos y resultados pueden tener diferentes tipos.


Sobrecarga 3:

C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)


La tercera sobrecarga no es muy útil IMO.
Lo mismo se puede escribir de manera más sucinta utilizando la sobrecarga 2 seguida por una función que transforma su resultado.


Las ilustraciones están adaptadas de este excelente blogpost .

Esto puede parecer una tontería, pero no he podido encontrar una buena explicación del Aggregate .

Buen significa corto, descriptivo, completo con un ejemplo pequeño y claro.


Además de todas las grandes respuestas que ya hemos incluido aquí, también lo he usado para guiar un elemento a través de una serie de pasos de transformación.

Si una transformación se implementa como Func<T,T> , puede agregar varias transformaciones a una List<Func<T,T>> y usar Aggregate para recorrer una instancia de T través de cada paso.

Un ejemplo más concreto.

Desea tomar un valor de string y recorrerlo a través de una serie de transformaciones de texto que se podrían construir mediante programación.

var transformationPipeLine = new List<Func<string, string>>(); transformationPipeLine.Add((input) => input.Trim()); transformationPipeLine.Add((input) => input.Substring(1)); transformationPipeLine.Add((input) => input.Substring(0, input.Length - 1)); transformationPipeLine.Add((input) => input.ToUpper()); var text = " cat "; var output = transformationPipeLine.Aggregate(text, (input, transform)=> transform(input)); Console.WriteLine(output);

Esto creará una cadena de transformaciones: Eliminar los espacios iniciales y finales -> eliminar el primer carácter -> eliminar el último carácter -> convertir a mayúsculas. Los pasos en esta cadena se pueden agregar, eliminar o reordenar según sea necesario, para crear cualquier tipo de canal de transformación que se requiera.

El resultado final de esta tubería específica, es que " cat " convierte en "A" .

Esto puede volverse muy poderoso una vez que te das cuenta de que T puede ser cualquier cosa . Esto podría usarse para transformaciones de imagen, como filtros, usando BitMap como ejemplo;


Agregado utilizado para sumar columnas en una matriz de enteros multidimensional

int[][] nonMagicSquare = { new int[] { 3, 1, 7, 8 }, new int[] { 2, 4, 16, 5 }, new int[] { 11, 6, 12, 15 }, new int[] { 9, 13, 10, 14 } }; IEnumerable<int> rowSums = nonMagicSquare .Select(row => row.Sum()); IEnumerable<int> colSums = nonMagicSquare .Aggregate( (priorSums, currentRow) => priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray() );

La selección con índice se usa dentro de la función de agregado para sumar las columnas coincidentes y devolver una nueva matriz; {3 + 2 = 5, 1 + 4 = 5, 7 + 16 = 23, 8 + 5 = 13}.

Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46 Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42

Pero contar el número de pistas en una matriz booleana es más difícil ya que el tipo acumulado (int) difiere del tipo fuente (bool); Aquí es necesaria una semilla para usar la segunda sobrecarga.

bool[][] booleanTable = { new bool[] { true, true, true, false }, new bool[] { false, false, false, true }, new bool[] { true, false, false, true }, new bool[] { true, true, false, false } }; IEnumerable<int> rowCounts = booleanTable .Select(row => row.Select(value => value ? 1 : 0).Sum()); IEnumerable<int> seed = new int[booleanTable.First().Length]; IEnumerable<int> colCounts = booleanTable .Aggregate(seed, (priorSums, currentRow) => priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray() ); Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2 Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2


Aprendí mucho de Jamiec''s respuesta de Jamiec''s .

Si lo único que necesita es generar una cadena CSV, puede intentar esto.

var csv3 = string.Join(",",chars);

Aquí hay una prueba con 1 millón de cuerdas.

0.28 seconds = Aggregate w/ String Builder 0.30 seconds = String.Join

El código fuente está here


Depende en parte de la sobrecarga de la que estés hablando, pero la idea básica es:

  • Comience con una semilla como el "valor actual"
  • Iterar sobre la secuencia. Para cada valor en la secuencia:
    • Aplicar una función especificada por el usuario para transformar (currentValue, sequenceValue) en (nextValue)
    • Establecer currentValue = nextValue
  • Devolver el valor currentValue

Puede encontrar útil la publicación de Aggregate en mi serie Edulinq , que incluye una descripción más detallada (incluidas las diversas sobrecargas) e implementaciones.

Un ejemplo simple es usar Aggregate como alternativa a Count :

// 0 is the seed, and for each item, we effectively increment the current value. // In this case we can ignore "item" itself. int count = sequence.Aggregate(0, (current, item) => current + 1);

O tal vez sumando todas las longitudes de las cadenas en una secuencia de cadenas:

int total = sequence.Aggregate(0, (current, item) => current + item.Length);

Personalmente, rara vez encuentro que Aggregate útil: los métodos de agregación "a medida" suelen ser lo suficientemente buenos para mí.


El agregado se utiliza básicamente para agrupar o resumir datos.

Según MSDN, "Función agregada aplica una función de acumulador en una secuencia".

Ejemplo 1: Suma todos los números en una matriz.

int[] numbers = new int[] { 1,2,3,4,5 }; int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);

* importante: el valor agregado inicial por defecto es el elemento 1 en la secuencia de recolección. es decir: el valor inicial de la variable total será 1 por defecto.

explicación variable

total: mantendrá el valor de suma (valor agregado) devuelto por la función.

nextValue: es el siguiente valor en la secuencia de la matriz. Este valor se agrega al valor agregado, es decir, al total.

Ejemplo 2: Agregar todos los elementos en una matriz. También establezca el valor inicial del acumulador para comenzar a sumar desde 10.

int[] numbers = new int[] { 1,2,3,4,5 }; int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);

explicación de los argumentos:

el primer argumento es el inicial (valor inicial, es decir, valor semilla) que se usará para comenzar la adición con el siguiente valor en la matriz.

El segundo argumento es una función que es una función que toma 2 int.

1.total: se mantendrá igual que antes del valor de resumen (valor agregado) devuelto por la función después del cálculo.

2.nextValue:: es el siguiente valor en la secuencia de matriz. Este valor se agrega al valor agregado, es decir, al total.

También la depuración de este código le dará una mejor comprensión de cómo funciona el agregado.


Esta es una explicación sobre el uso de Aggregate en una API Fluent como Linq Sorting.

var list = new List<Student>(); var sorted = list .OrderBy(s => s.LastName) .ThenBy(s => s.FirstName) .ThenBy(s => s.Age) .ThenBy(s => s.Grading) .ThenBy(s => s.TotalCourses);

y veamos que queremos implementar una función de clasificación que tome un conjunto de campos, esto es muy fácil usando Aggregate lugar de un bucle for, como esto:

public static IOrderedEnumerable<Student> MySort( this List<Student> list, params Func<Student, object>[] fields) { var firstField = fields.First(); var otherFields = fields.Skip(1); var init = list.OrderBy(firstField); return otherFields.Skip(1).Aggregate(init, (resultList, current) => resultList.ThenBy(current)); }

Y podemos usarlo así:

var sorted = list.MySort( s => s.LastName, s => s.FirstName, s => s.Age, s => s.Grading, s => s.TotalCourses);


La definición más fácil de entender de Aggregate es que realiza una operación en cada elemento de la lista teniendo en cuenta las operaciones anteriores. Es decir, realiza la acción en el primer y segundo elemento y lleva el resultado hacia adelante. Luego opera sobre el resultado anterior y el tercer elemento y lleva adelante. etc.

Ejemplo 1. Sumando números.

var nums = new[]{1,2,3,4}; var sum = nums.Aggregate( (a,b) => a + b); Console.WriteLine(sum); // output: 10 (1+2+3+4)

Esto suma 1 y 2 para hacer 3 . Luego agrega 3 (resultado del anterior) y 3 (siguiente elemento en secuencia) para hacer 6 . Luego suma 6 y 4 para hacer 10 .

Ejemplo 2. crear un csv desde una matriz de cadenas

var chars = new []{"a","b","c", "d"}; var csv = chars.Aggregate( (a,b) => a + '','' + b); Console.WriteLine(csv); // Output a,b,c,d

Esto funciona de la misma manera. Concatenar a coma y b para hacer a,b . Luego concatena a,b con una coma c para hacer a,b,c . y así.

Ejemplo 3. Multiplicando números usando una semilla

Para completar, hay una overload de Aggregate que toma un valor semilla.

var multipliers = new []{10,20,30,40}; var multiplied = multipliers.Aggregate(5, (a,b) => a * b); Console.WriteLine(multiplied); //Output 1200000 ((((5*10)*20)*30)*40)

Al igual que los ejemplos anteriores, esto comienza con un valor de 5 y lo multiplica por el primer elemento de la secuencia 10 dando un resultado de 50 . Este resultado se lleva adelante y se multiplica por el siguiente número en la secuencia 20 para dar un resultado de 1000 . Esto continúa a través de los 2 elementos restantes de la secuencia.

Ejemplos en vivo: http://rextester.com/ZXZ64749
Docs: http://msdn.microsoft.com/en-us/library/bb548651.aspx

Apéndice

El ejemplo 2, arriba, usa la concatenación de cadenas para crear una lista de valores separados por una coma. Esta es una forma simplista de explicar el uso del Aggregate que fue la intención de esta respuesta. Sin embargo, si utiliza esta técnica para crear realmente una gran cantidad de datos separados por comas, sería más apropiado usar un StringBuilder , y esto es totalmente compatible con Aggregate usa la sobrecarga generada para iniciar el StringBuilder .

var chars = new []{"a","b","c", "d"}; var csv = chars.Aggregate(new StringBuilder(), (a,b) => { if(a.Length>0) a.Append(","); a.Append(b); return a; }); Console.WriteLine(csv);

Ejemplo actualizado: http://rextester.com/YZCVXV6464


Todos han dado su explicación. Mi explicación es así.

El método agregado aplica una función a cada elemento de una colección. Por ejemplo, tengamos la colección {6, 2, 8, 3} y la función Agregar (operador +) hace (((6 + 2) +8) +3) y devuelve 19

var numbers = new List<int> { 6, 2, 8, 3 }; int sum = numbers.Aggregate(func: (result, item) => result + item); // sum: (((6+2)+8)+3) = 19

En este ejemplo, se pasa el método denominado Agregar en lugar de la expresión lambda.

var numbers = new List<int> { 6, 2, 8, 3 }; int sum = numbers.Aggregate(func: Add); // sum: (((6+2)+8)+3) = 19 private static int Add(int x, int y) { return x + y; }


Una definición breve y esencial podría ser esta: el método de extensión Linq Aggregate permite declarar un tipo de función recursiva aplicada a los elementos de una lista, cuyos operandos son dos: los elementos en el orden en que están presentes en la lista, un elemento a la vez, y el resultado de la iteración recursiva anterior o nada si aún no es recursiva.

De esta manera, puede calcular el factorial de números o concatenar cadenas.


El agregado súper corto funciona como el pliegue en Haskell / ML / F #.

Un poco más largo .Max (), .Min (), .Sum (), .Average () todo itera sobre los elementos en una secuencia y los agrega usando la función agregada respectiva. .Aggregate () es un agregador generalizado ya que le permite al desarrollador especificar el estado de inicio (también conocido como semilla) y la función agregada.

Sé que pediste una breve explicación, pero pensé que mientras otros daban un par de respuestas cortas, pensé que quizás estarías interesado en una más larga.

Versión larga con código Una forma de ilustrar cómo se puede mostrar cómo se implementa la Desviación estándar de muestra una vez usando foreach y una vez usando .Aggregate. Nota: no he priorizado el rendimiento aquí, así que repetí varias veces innecesariamente

Primero, una función auxiliar que se usa para crear una suma de distancias cuadráticas:

static double SumOfQuadraticDistance (double average, int value, double state) { var diff = (value - average); return state + diff * diff; }

Luego muestre la desviación estándar usando ForEach:

static double SampleStandardDeviation_ForEach ( this IEnumerable<int> ints) { var length = ints.Count (); if (length < 2) { return 0.0; } const double seed = 0.0; var average = ints.Average (); var state = seed; foreach (var value in ints) { state = SumOfQuadraticDistance (average, value, state); } var sumOfQuadraticDistance = state; return Math.Sqrt (sumOfQuadraticDistance / (length - 1)); }

Luego, una vez utilizando. Agregado:

static double SampleStandardDeviation_Aggregate ( this IEnumerable<int> ints) { var length = ints.Count (); if (length < 2) { return 0.0; } const double seed = 0.0; var average = ints.Average (); var sumOfQuadraticDistance = ints .Aggregate ( seed, (state, value) => SumOfQuadraticDistance (average, value, state) ); return Math.Sqrt (sumOfQuadraticDistance / (length - 1)); }

Tenga en cuenta que estas funciones son idénticas, excepto en cómo se calcula sumOfQuadraticDistance:

var state = seed; foreach (var value in ints) { state = SumOfQuadraticDistance (average, value, state); } var sumOfQuadraticDistance = state;

Versus:

var sumOfQuadraticDistance = ints .Aggregate ( seed, (state, value) => SumOfQuadraticDistance (average, value, state) );

Entonces, lo que hace .Aggregate es que encapsula este patrón agregador y espero que la implementación de .Aggregate se vea algo como esto:

public static TAggregate Aggregate<TAggregate, TValue> ( this IEnumerable<TValue> values, TAggregate seed, Func<TAggregate, TValue, TAggregate> aggregator ) { var state = seed; foreach (var value in values) { state = aggregator (state, value); } return state; }

El uso de las funciones de desviación estándar se vería así:

var ints = new[] {3, 1, 4, 1, 5, 9, 2, 6, 5, 4}; var average = ints.Average (); var sampleStandardDeviation = ints.SampleStandardDeviation_Aggregate (); var sampleStandardDeviation2 = ints.SampleStandardDeviation_ForEach (); Console.WriteLine (average); Console.WriteLine (sampleStandardDeviation); Console.WriteLine (sampleStandardDeviation2);

En mi humilde opinión

Entonces, ¿el agregado ayuda a la legibilidad? En general, me encanta LINQ porque creo que .Where, .Select, .OrderBy y así sucesivamente ayuda en gran medida a la legibilidad (si se evitan las selecciones jerárquicas en línea). El agregado tiene que estar en Linq por razones de integridad, pero personalmente no estoy tan convencido de que el agregado agregue legibilidad en comparación con un foreach bien escrito.