Quelle est la manière C # -idiomatique d’appliquer un opérateur sur deux listes?

J’ai l’habitude de faire ça (d’autres langues):

a = 1, 2, 3; b = 5, 1, 2; c = a * b; // c = 5, 2, 6 

Cela prend deux listes de taille égale et applique une fonction à leurs membres, un à la fois, pour obtenir une liste des résultats. Cela pourrait être une fonction aussi simple que la multiplication (ci-dessus) ou quelque chose de plus complexe:

  c = b>a ? ba : 0; // c = 4, 0, 0 

Je peux penser à différentes manières de faire cela en C #, mais je ne suis pas sûr de savoir comment un programmeur formé par C # le ferait. Quelle est la bonne façon d’y parvenir dans le monde C #?

(La seule partie dont je parle est où c = f(a,b) . Je suis familier avec la création de listes et l’access à leurs éléments.)

 var c = a.Zip(b, (x, y) => x * y); 

Pour le plus complexe après votre édition:

 var c = a.Zip(b, (x, y) => x > y ? x - y : 0); 

Notez que Zip est une méthode d’extension à la fois de Enumerable qui agit sur IEnumerable et de Queryable qui agit sur IQueryable , il est donc possible que le lambda puisse être traité par un fournisseur de requêtes donné. être traité comme une requête SQL sur une firebase database ou d’une autre manière que celle en mémoire dans .NET.

Quelqu’un a mentionné que c’était nouveau avec 4.0 dans les commentaires. Ce n’est pas difficile à mettre en œuvre pour 3.5 vous-même:

 public class MyExtraLinqyStuff { public static IEnumerable Zip(this IEnumerable first, IEnumerable second, Func resultSelector) { //Do null checks immediately; if(first == null) throw new ArgumentNullException("first"); if(second == null) throw new ArgumentNullException("second"); if(resultSelector == null) throw new ArgumentNullException("resultSelector"); return DoZip(first, second, resultSelector); } private static IEnumerable DoZip(this IEnumerable first, IEnumerable second, Func resultSelector) { using(var enF = first.GetEnumerator()) using(var enS = second.GetEnumerator()) while(enF.MoveNext() && enS.MoveNext()) yield return resultSelector(enF.Current, enS.Current); } } 

Pour .NET2.0 ou .NET3.0, vous pouvez avoir la même chose, mais pas comme méthode d’extension, qui répond à une autre question des commentaires. Il n’y avait pas vraiment de manière idiomatique de faire de telles choses dans .NET à ce moment-là, ou du moins pas avec un consensus ferme parmi ceux d’entre nous qui codaient alors dans .NET. Certains d’entre nous avaient des méthodes comme celles ci-dessus dans nos boîtes à outils (mais pas évidemment des méthodes d’extension), mais c’était plus que d’autres langues (par exemple, je connaissais des choses comme La STL du C ++, mais ce n’était guère la seule source d’inspiration possible)

En supposant que .Net 3.5 avec des listes de même longueur:

 var a = new List() { 1, 2, 3 }; var b = new List() { 5, 1, 2 }; var c = a.Select((x, i) => b[i] * x); 

Résultat:

5

2

6

DotNetFiddle.Net Exemple

Si vous n’utilisez pas .NET 4.0, voici comment écrire votre propre méthode d’extension pour faire un Zip.

 static IEnumerable Zip(this IEnumerable first, IEnumerable second, Func resultSelector) { using (IEnumerator e1 = first.GetEnumerator()) using (IEnumerator e2 = second.GetEnumerator()) { while (e1.MoveNext() && e2.MoveNext()) { yield return resultSelector(e1.Current, e2.Current); } } } 

Pour les versions .NET sans LINQ, je recommanderais une boucle for pour accomplir ceci:

 List list1 = new List(){4,7,9}; List list2 = new List(){11,2,3}; List newList = new List(); for (int i = 0; i < list1.Count; ++i) { newList.Add(Math.Max(list1[i], list2[i])); } 

Cela suppose, bien sûr, que les listes aient la même taille et ne changent pas. Si vous connaissez la taille de la liste à l'avance, vous pouvez également l'instancier à la bonne taille, puis définissez simplement l'élément pendant la boucle.