Exercice – Apprivoiser la généricité et les λ

Quelques exercices pour apprivoiser la généricité et les λ avec C#. Notez que certains des exemples utilisent des uplets.

Rappel – Expressions λ

En C#, les expressions λ permettent d'exprimer simplement des fonctions anonymes. Par exemple, une λ qui accepte un nombre et retourne son carré serait :

x => x * x

Avec C#, on peut entreposer une λ dans un Func. Ainsi, ceci affichera 9 et Pair :

static void Main()
{
   Func<int,int> carré = x => x * x;
   // exemple simpliste de λ qui ne se limite pas à une seule expression (avec accolades)
   Func<int,int,bool> sommePaire = (x,y) =>
   {
      int somme = x + y;
      return somme % 2 == 0;
   };
   Console.WriteLine(carré(3));
   if (sommePaire(3,5))
   {
      Console.WriteLine("Pair");
   }
}

... affichera 9 et Pair.

Quelques exercices

Voici quelques exercices que je vous recommande de faire. Prenez soin de tester chacune de vos réponses avec une variété d'intrants valides (donc ne brisant pas les préconditions des fonctions), incluant des valeurs pathologiques (liste vide, éléments absents, éléments dont on trouve plusieurs occurrences, etc.).

Il se peut que certaines des fonctions ci-dessous soient utiles pour exprimer d'autres fonctions du lot. Si vous pouvez le faire sans perte d'efficacité, alors n'hésitez pas!

EX00 – Écrivez l'algorithme Permuter<T>(ref T a, ref T b) qui permutera les valeurs de a et de b, de telle sorte que le programme suivant :

static void Main()
{
   int i0 = 2, i1 = 3;
   Console.WriteLine($"Avant permutation : {i0}, {i1}");
   Permuter(ref i0, ref i1);
   Console.WriteLine($"Après permutation : {i0}, {i1}");
   double d0 = 2.5, d1 = 3.5;
   Console.WriteLine($"Avant permutation : {d0}, {d1}");
   Permuter(ref d0, ref d1);
   Console.WriteLine($"Après permutation : {d0}, {d1}");
   string s0 = "allo", s1 = "toi";
   Console.WriteLine($"Avant permutation : \"{s0}\", \"{s1}\"");
   Permuter(ref s0, ref s1);
   Console.WriteLine($@"Après permutation : ""{s0}"", ""{s1}""");
}

... affiche ce qui suit :

Avant permutation : 2,3
Après permutation : 3,2
Avant permutation : 2.5,3.5
Après permutation : 3.5,2.5
Avant permutation : "allo","toi"
Après permutation : "toi","allo"

EX01 – Écrivez l'algorithme Transformer<T,U>(List<T> src, Func<T,U> fct) qui retourne une List<U> contenant un équivalent des éléments de la List<T> reçue en paramètre, mais transformés par application de la fonction fct, de telle sorte que le programme suivant :

static void Main()
{
   var lst0 = new List<int>(){ 2,3,5,7,11 };
   var lst1 = Transformer(lst0, x => -1.0 * x * x);
   foreach(double x in lst1)
      Console.Write($"{x} ");
   Console.WriteLine();
   foreach(string s in Transformer(new List<string>(){ "j'aime", "mon", "prof" }, s => s.ToUpper() + "!"))
      Console.Write($"{s} ");
   Console.WriteLine();
   List<(bool signe, int val)> lst2 = Transformer(new List<int>() {2, -3, 5, -7, 11}, n => ((Math.Sign(n) < 0), (int)Math.Abs(n)));
   foreach(var p in lst2)
      Console.WriteLine($"{(p.signe? "moins" : "plus")} {p.val}");
}

... affiche ce qui suit :

-4 -9 -25 -49 -121 
J'AIME! MON! PROF!
plus 2
moins 3
plus 5
moins 7
plus 11

EX02 – Écrivez l'algorithme SupprimerDoublons<T>(List<T> src) qui reçoit en paramètre une List<T> préalablement triée (il s'agit d'une précondition de la fonction), et retourne une List<T> contenant les mêmes valeurs que la List<T> originale, mais exempte de doublons, de telle sorte que le programme suivant :

static void Afficher<T>(List<T> lst)
{
   foreach(T obj in lst)
      Console.Write($"{obj} ");
   Console.WriteLine();
}
public static void Main()
{
   Afficher(SupprimerDoublons(new List<int>()));
   Afficher(SupprimerDoublons(new List<int>(){ 2 }));
   Afficher(SupprimerDoublons(new List<int>(){ 2, 2 }));
   Afficher(SupprimerDoublons(new List<int>(){ 2,3,3,3,5,7,11 }));
   Afficher(SupprimerDoublons(new List<int>(){ 2,2,3,5,5,7,11 }));
   Afficher(SupprimerDoublons(new List<int>(){ 2,3,5,7,7,11 }));
   Afficher(SupprimerDoublons(new List<int>(){ 2,3,5,7,11,11 }));
}

... affiche ce qui suit (notez la première ligne qui est vide, car nous avons supprimé les doublons... d'une séquence vide!) :


2 
2 
2 3 5 7 11 
2 3 5 7 11 
2 3 5 7 11 
2 3 5 7 11

EX03 – Écrivez l'algorithme Cumuler<T,U>(List<T> src, Func<U,T,U> accum, U init) qui reçoit en paramètre une List<T>, une fonction applicable à un U et à un T et retournant un U, de même qu'une valeur initiale de type U, et retourne l'accumulation , de telle sorte que le programme suivant :

public static void Main()
{
   Console.WriteLine($"1+2+3+4+5 == {Cumuler(new List<int>(){1,2,3,4,5}, (x,y) => x+y, 0)}");
   Console.WriteLine($"1*2*3*4*5 == {Cumuler(new List<int>(){1,2,3,4,5}, (x,y) => x*y, 1.0)}");
   Console.WriteLine($"min(2,-3,5,-7,11) == {Cumuler(new List<int>(){2,-3,5,-7,9}, (x,y) => Math.Min(x,y), int.MaxValue)}");
   var mots = new List<string>() { "yo", "man", "genre" };
   Console.Write("La somme des longueurs des mots (");
   foreach (string s in mots)
      Console.Write($"{s} ");
   Console.WriteLine($"\b) est : {Cumuler(mots, (lg, s) => lg + s.Length, 0)}");
}

... affiche ce qui suit :

1+2+3+4+5 == 15
1*2*3*4*5 == 120
min(2,-3,5,-7,11) == -7
La somme des longueurs des mots (yo man genre) est : 10

EX04 – Écrivez la fonction Trouver<T>(List<T> src, T val) qui retournera l'indice de la première occurrence de val dans src, ou -1 si aucune occurrence n'est trouvée.

EX05 – Écrivez la fonction TrouverSi<T>(List<T> src, Func<T,bool> pred) qui retournera l'indice du premier élément de src satisfaisant le prédicat pred, ou -1 si aucun élément ne satisfaisant pred n'est trouvé.

EX06 – Écrivez la fonction Filtrer<T>(List<T> src, T val) qui retournera une List<T> contenant les mêmes éléments que src, dans le même ordre, à ceci près que toutes les occurrences de la valeur val en auront été supprimées.

EX07 – Écrivez la fonction FiltrerSi<T>(List<T> src, Func<T,bool> pred) qui retournera une List<T> contenant les mêmes éléments que src, dans le même ordre, à ceci près que tous les éléments respectant le prédicat pred auront été supprimés.

EX08 – Écrivez la fonction Concaténer<T>(List<T> lst0, List<T> lst1) qui retournera une List<T> contenant les mêmes éléments que lst0, dans l'ordre, suivis des éléments de lst1, dans l'ordre.

EX09 – Écrivez la fonction Remplacer<T>(List<T> src, T pré, T post) qui retournera une List<T> contenant les mêmes éléments que src, dans l'ordre, mais dont chaque occurrence de pré aura été remplacée par post.

EX10 – Écrivez la fonction RemplacerSi<T>(List<T> src, Func<T,bool> pred, T post) qui retournera une List<T> contenant les mêmes éléments que src, dans l'ordre, mais dont chaque élément satisfaisant le prédicat pred aura été remplacé par post.

Certaines des fonctions ci-dessus peuvent être exprimées en termes d'une autre, plus générale, et ce sans perte de généralité ou d'efficacité. Combien serez-vous capables d'en identifier?


Valid XHTML 1.0 Transitional

CSS Valide !