Skip to content

Exercices

Consignes

  • Activer la null safety
  • Ne pas utiliser les fonctions de tri de .Net

Collections

Partie 1

Utiliser foreach si besoin (et ne pas utiliser LINQ: Select, etc.):

  1. Créer une fonction qui prend une liste d'entiers en argument et affiche uniquement les entiers pairs.
  2. Créer une fonction qui prend une liste d'entiers en argument et retourne la liste des entiers pairs.
  3. Créer une fonction qui prend une chaîne de caractères en argument (string) et retourne la liste de ses consonnes (pour rappel, les voyelles sont a, i, u, e, o et y).
  4. Créer une fonction qui prend une liste de chaînes de caractères et retourne la liste des longueurs de chaque chaîne.
    • Par exemple: ["hello", "C#"] retourne [5, 2]
  5. Définir un record Person avec les propriétés Name et Age. Créer une liste de personnes et:
    1. afficher les personnes dont l'âge est supérieur à 18 ans.
    2. afficher les personnes dont le nom commence par la lettre 'A'.
    3. Créer une fonction qui change le nom en Camel case de chaque personne.
      • Par exemple: Person("john doe", 20) devient Person("John Doe", 20)
    4. Créer un fonction qui donne l'âge moyen des personnes.
Corrections
1

2

3

Partie 2

Utiliser LINQ pour la partie algorithmie. Utiliser foreach uniquement pour l'affichage.

  1. Refaire les exos de la partie 1 avec LINQ.
  2. Créer une fonction qui prend un entier n. La fonction génère une liste de n entiers aléatoires entre 0 et 100 et affiche le min, le max, la somme et la moyenne.
    • Par exemple: si n = 5 génère le tableau [10, 20, 30, 40, 50], afficher min: 10, max: 50, sum: 150, avg: 30
  3. Créer une fonction qui prend une liste de chaînes de caractères et retourne la liste des chaînes de caractères triées par ordre croissant de longueur. (💡 utiliser OrderBy((s) => s.Length)).
    • Par exemple: ["hello", "C#", "world"] retourne ["C#", "hello", "world"]
  4. Créer une fonction qui prend une liste de chaînes de caractères et retourne le nombre de caractères total de toutes les chaînes.
    • Par exemple: ["hello", "C#", "world"] retourne 12
  5. Créer une fonction qui prend une liste de chaînes de caractères et retourne le nombre de mots qui ont une longueur supérieure à 3.
    • Par exemple: ["hello", "C#", "world"] retourne 2
  6. Définir un record Ninja avec les propriétés Name, Hp et IsHokage. Créer une liste de ninjas et:
    • Afficher les noms des Hokage.
      • Par exemple: Ninja("Naruto", 150, true) et Ninja("Sasuke", 120, false) retourne Naruto
    • Afficher le nombre de ninjas qui ont plus de 100 points de vie.
      • Par exemple: Ninja("Naruto", 150, true) et Ninja("Sasuke", 120, false) retourne 2
    • Afficher le nom ninja qui a le plus de points de vie.
      • Par exemple: Ninja("Naruto", 150, true) et Ninja("Sasuke", 120, false) retourne Naruto
Corrections
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;

void RunQ1()
{
  void ShowEvenNumbers(List<int> numbers)
  {
    var evenNumbers = from n in numbers where n % 2 == 0 select n;
    Console.WriteLine(string.Join(",", evenNumbers));
  }
  Console.WriteLine("Exercise 1");
  ShowEvenNumbers([1, 2, 3, 4, 5, 6, 7]);
  // Enumerable.Range equivalent of Python range with less features
  ShowEvenNumbers(Enumerable.Range(-10, 20).ToList());
}

void RunQ2()
{
  // Avec la method syntax
  List<int> GetEvenNumbers(List<int> numbers)
  {
    var evenNumbers = numbers.Where(n => n % 2 == 0);
    return evenNumbers.ToList();
  }
  // Avec la query syntax et avec un single statement function
  List<int> GetEvenNumbers2(List<int> numbers) =>
    (from n in numbers where n % 2 == 0 select n).ToList();

  List<int> numbers = Enumerable.Range(1, 10).ToList();
  Console.WriteLine("Exercise 2");
  Console.WriteLine(string.Join(",", GetEvenNumbers(numbers)));
  Console.WriteLine(string.Join(",", GetEvenNumbers2(numbers)));
}

void RunQ3()
{
  string GetConsonants(string input)
  {
    string lowerCased = input.ToLower();
    var consonants = lowerCased.Where(c => !"aeiuyo".Contains(c));
    return string.Join("", consonants);
  }

  string GetConsonants2(string input) =>
    string.Join("", input.ToLower().Where(c => !"aeiuyo".Contains(c)));
  Console.WriteLine("Exercise 3");
  Console.WriteLine(GetConsonants("Hello"));
  Console.WriteLine(GetConsonants("BBBBCCCC"));
  Console.WriteLine(GetConsonants("aeae"));
  Console.WriteLine(GetConsonants2("Hello"));
  Console.WriteLine(GetConsonants2("BBBBCCCC"));
  Console.WriteLine(GetConsonants2("aeae"));
}

void RunQ4()
{
  List<int> GetLengths(List<string> texts) =>
    (from text in texts select text.Length).ToList();
  Console.WriteLine("Exercise 4");
  Console.WriteLine(string.Join(", ", GetLengths(["I", "Love", "C#"])));
  Console.WriteLine(string.Join(", ", GetLengths(["LINQ", "Rocks"])));
}

void RunQ5()
{
  string ToCamelCaseName(string name)
  {
    var transformed = name.Select((c, index) => index == 0 || name[index - 1] == ' ' ? char.ToUpper(c) : c);
    return string.Join("", transformed);
  }
  void PrintResults(List<Person> persons)
  {
    Console.WriteLine($"input list: {string.Join(",", persons)}");
    var olderThan18 = persons.Where(p => p.Age > 18);
    Console.WriteLine($"Older than 18: {string.Join(", ", olderThan18)}");
    var startWithAPersons = from p in persons where p.Name.Length > 1 && p.Name[0] == 'A' select p;
    Console.WriteLine($"Start with A: {string.Join(", ", startWithAPersons)}");
    var camelCased = persons.Select(p => new Person(ToCamelCaseName(p.Name), p.Age));
    Console.WriteLine($"Camel cased: {string.Join(", ", camelCased)}");
  }
  List<Person> persons = [new("Kakashi", 50), new("Arata", 23), new("j bap", 2)];
  PrintResults(persons);
  Random rng = new();
  List<Person> randomPersons = Enumerable.Range(1, 5)
    .Select(
      i => new Person($"person number {i}", rng.Next(10, 30))
    ).ToList();
  PrintResults(randomPersons);
}

RunQ1();
RunQ2();
RunQ3();
RunQ4();
RunQ5();

record Person(string Name, int Age);

Fichiers

  1. Utiliser les StreamReader et / ou StreamWriter pour lire et un fichier et:
    • Afficher le nombre de caractères de chaque ligne de ce fichier
    • Génère un fichier qui ajoute un point à la fin de chaque ligne du fichier d'origine si elle n'est pas déjà présente
    • Génère un fichier qui contient la ligne qui a le plus de caractères dans le fichier lu
    • Génère un fichier qui contient les lignes du fichier lu triés par ordre croissant le nombre de caractères de la ligne
    • Génère un fichier qui contient les lignes du fichier lu triés par ordre croissant du dictionnaire
  2. Utiliser un StreamWriter pour écrire dans un fichier le contenu d'une chaîne de caractères au format pyramidal. Par exemple "Je-fais-des-exos-de-C#" donne le fichier (le dernier caractère sera ignoré car il ne permet pas de faire une pyramide):
    J
    e-
    fai
    s-de
    s-exo
    s-de-C
    
  3. Définir votre propose classe de journalisation (Logger) qui va servir à écrire des logs dans un fichier.
    • La classe prend en argument du constructeur le nom du fichier de logs
    • La classe propose les méthodes: Info(String message) et Warn(String message). Chacune des méthodes ajoute une ligne au fichier avec le format:
      • Si c'est une Info: {date et heure actuelle} ℹ️ {message}
      • Si c'est un Warn: {date et heure actuelle} ⚠️ {message}
    • ⚠️: Attention à bien fermer le fichier une fois l'écriture du log faite (pour le libérer)
    • Créer une instance de la classe Logger et ajouter quelques logs
    • Ajouter les méthodes Error(String message) et Debug(String message) avec un format adapté et basé sur le même principe que les méthodes définies plus haut.
    • Définir une méthode ShowAllWarnings() qui affiche tous les logs de type Warn présents dans le fichier de log.
    • Est-ce que vous voyez des inconvénients / contraintes avec cette méthode de journalisation ?
  4. Faire le problème 1 de advent of code 2023 en chargeant les données d'entrée depuis un fichier.
    • Par exemple pour l'exercice 1 du problème, créer à la main (avec vscode ou l'explorateur) un fichier input1.txt avec le contenu ci-dessous et le lire avec un StreamReader pour résoudre le problème.
      1abc2
      pqr3stu8vwx
      a1b2c3d4e5f
      treb7uchet
      
Corrections
static void PrintCountPerLine(string filePath)
{
  using StreamReader sr = new(filePath);
  string? line = sr.ReadLine();
  while (line != null)
  {
    Console.WriteLine(line.Length);
    line = sr.ReadLine();
  }
}

static void AddDotIfNotPresent(string filePath)
{
  using StreamReader sr = new(filePath);
  using StreamWriter sw = new("dotted_file.txt");
  string? line = sr.ReadLine();
  while (line != null)
  {
    if (!line.EndsWith('.'))
    {
      line += '.'; // autre façon: line = $"{line}.";
    }
    sw.WriteLine(line);
    line = sr.ReadLine();
  }
}

static void WriteLongestLine(string filePath)
{
  using StreamReader sr = new(filePath);
  string? line = sr.ReadLine();
  string? longestLine = line;
  while (line != null)
  {
    line = sr.ReadLine();
    if (longestLine?.Length < line?.Length)
    {
      longestLine = line;
    }
  }
  if (longestLine != null)
  {
    using StreamWriter sw = new("longest_line.txt");
    sw.WriteLine(longestLine);
  }
}

string filePath = "input_file.txt";
PrintCountPerLine(filePath);
AddDotIfNotPresent(filePath);
WriteLongestLine(filePath);

Série 2 (programmation asynchrone)

En C#, la méthode recommandée pour exécuter des traitements asynchrones (qui sont mis en oeuvre habituellement avec les threads et les process) consiste à utiliser “Task.Run(() => { // traitement async })”. On dit que c’est une programmation asynchrone basée sur les tâches.

  1. Créer deux tâches asynchrones (en faisant deux Task.Run)
    • La première affiche un valeur qui s’incrémente de 1 à 1000 (une boucle for qui affiche i à chaque itération)
    • La deuxième affiche un valeur qui va de -1 à -1000 (une boucle for de -1 à -1000 avec un writeline)
    • Pour laisser le temps aux tâches concurrentes de finir avant la fin du thread principal, ajouter un await Task.Delay(1000); à la fin de la tâche principale
    • Exécuter le programme plusieurs fois, que constatez vous ?
    • Afficher finished 1 juste à la fin de la première tâche (sans utiliser await et ContinueWith)
    • -> solution: utiliser une callback sous forme d’une fonction passée en paramètre
    • En utilisant ContinueWith afficher finished 1 juste après la fin de la première tâche
    • Remplacer chaque Task.Run par await Task.Run. Que constatez-vous ?
    • En utilisant await, comment afficher “finished 2” dès la fin de la deuxième tâche ?
  2. Dans la première tâche, ajouter une instruction ‘return “tartatin”;’ après la boucle for. Ensuite remplacer le premier await Task.Run par un var result = await Task.Run.
    • Quel est le type et la valeur de result ?
    • Enlever await, c’est à dire remplacer var result = await Task.Run par var result = Task.Run
    • Quel est le type et la valeur de result ?
    • A quoi sert await ? Quel est sa relation avec le type Task ?