Utilisation de LINQWorking with LINQ

IntroductionIntroduction

Ce tutoriel vous présente les fonctionnalités de .NET Core et du langage C#.This tutorial teaches you features in .NET Core and the C# language. Vous apprendrez à :You’ll learn:

  • Générer des séquences avec LINQ.How to generate sequences with LINQ.
  • écrire des méthodes utilisables dans des requêtes LINQ ;How to write methods that can be easily used in LINQ queries.
  • faire la distinction entre l’évaluation stricte et l’évaluation paresseuse.How to distinguish between eager and lazy evaluation.

Vous apprendrez ces techniques en créant une application qui illustre l’une des compétences de base de tout magicien : le mélange faro.You'll learn these techniques by building an application that demonstrates one of the basic skills of any magician: the faro shuffle. En quelques mots, le mélange faro est une technique qui consiste à diviser un paquet de cartes en deux moitiés exactes, puis à intercaler une carte sur deux de chacune des deux moitiés de façon à reconstruire le jeu d’origine.Briefly, a faro shuffle is a technique where you split a card deck exactly in half, then the shuffle interleaves each one card from each half to rebuild the original deck.

Les magiciens utilisent cette technique parce que chaque carte se trouve à un emplacement connu après chaque mélange, suivant un motif répétitif.Magicians use this technique because every card is in a known location after each shuffle, and the order is a repeating pattern.

Dans notre cas, c’est une façon plaisante d’envisager la manipulation de séquences de données.For your purposes, it is a light hearted look at manipulating sequences of data. L’application que vous allez créer construira un jeu de cartes, puis effectuera une suite de mélanges, en affichant la séquence à chaque fois.The application you'll build will construct a card deck, and then perform a sequence of shuffles, writing the sequence out each time. Vous comparerez également le nouvel ordre à l’ordre d’origine.You'll also compare the updated order to the original order.

Ce didacticiel comporte plusieurs étapes.This tutorial has multiple steps. Après chaque étape, vous pourrez exécuter l’application et voir la progression.After each step, you can run the application and see the progress. Vous pouvez également voir l’exemple terminé dans le dépôt GitHub dotnet/samples.You can also see the completed sample in the dotnet/samples GitHub repository. Pour obtenir des instructions de téléchargement, consultez Exemples et didacticiels.For download instructions, see Samples and Tutorials.

PrérequisPrerequisites

Vous devez configurer votre ordinateur pour exécuter .NET Core.You’ll need to setup your machine to run .NET core. Vous trouverez les instructions d’installation sur la page .NET Core.You can find the installation instructions on the .NET Core page. Vous pouvez exécuter cette application sous Windows, Ubuntu Linux, OS X ou dans un conteneur Docker.You can run this application on Windows, Ubuntu Linux, OS X or in a Docker container. Vous devez installer l’éditeur de code de votre choix.You’ll need to install your favorite code editor. Les descriptions ci-dessous utilisent Visual Studio Code, un éditeur open source et multiplateforme.The descriptions below use Visual Studio Code which is an open source, cross platform editor. Cependant, vous pouvez utiliser les outils avec lesquels vous êtes le plus à l’aise.However, you can use whatever tools you are comfortable with.

Création de l’applicationCreate the Application

La première étape consiste à créer une nouvelle application.The first step is to create a new application. Ouvrez une invite de commandes et créez un nouveau répertoire pour votre application.Open a command prompt and create a new directory for your application. Réglez-le comme répertoire actuel.Make that the current directory. Saisissez la commande dotnet new console à l’invite.Type the command dotnet new console at the command prompt. Elle crée les fichiers de démarrage d’une application « Hello World » de base.This creates the starter files for a basic "Hello World" application.

Si vous n’avez jamais utilisé C#, ce didacticiel explique la structure d’un programme C#.If you've never used C# before, this tutorial explains the structure of a C# program. Vous pouvez le lire, puis revenir ici pour en savoir plus sur LINQ.You can read that and then return here to learn more about LINQ.

Création du jeu de donnéesCreating the Data Set

Avant de commencer, vérifiez que les lignes suivantes figurent en haut du fichier Program.cs généré par dotnet new console :Before you begin, make sure that the following lines are at the top of the Program.cs file generated by dotnet new console:

// Program.cs
using System;
using System.Collections.Generic;
using System.Linq;

Si ces trois lignes (instructions using) ne figurent pas en haut du fichier, le programme ne se compilera pas.If these three lines (using statements) aren't at the top of the file, our program will not compile.

Maintenant que vous avez toutes les références dont vous avez besoin, réfléchissons à ce qui compose un jeu de cartes.Now that you have all of the references that you'll need, consider what constitutes a deck of cards. En général, un jeu de cartes à jouer comporte quatre couleurs, et chaque couleur a treize valeurs.Commonly, a deck of playing cards has four suits, and each suit has thirteen values. On pourrait envisager de créer directement une classe Card et de remplir manuellement une collection d’objets Card.Normally, you might consider creating a Card class right off the bat and populating a collection of Card objects by hand. Avec LINQ, il est possible d’être plus concis que cela.With LINQ, you can be more concise than the usual way of dealing with creating a deck of cards. Au lieu de créer une classe Card, vous pouvez créer deux séquences pour représenter respectivement les couleurs et les rangs.Instead of creating a Card class, you can create two sequences to represent suits and ranks, respectively. Créez deux méthodes d’itération simples qui génèreront les rangs et les couleurs sous forme de IEnumerable<T> de chaînes :You'll create a really simple pair of iterator methods that will generate the ranks and suits as IEnumerable<T>s of strings:

// Program.cs
// The Main() method

static IEnumerable<string> Suits()
{
    yield return "clubs";
    yield return "diamonds";
    yield return "hearts";
    yield return "spades";
}

static IEnumerable<string> Ranks()
{
    yield return "two";
    yield return "three";
    yield return "four";
    yield return "five";
    yield return "six";
    yield return "seven";
    yield return "eight";
    yield return "nine";
    yield return "ten";
    yield return "jack";
    yield return "queen";
    yield return "king";
    yield return "ace";
}

Placez-les sous la méthode Main dans votre fichier Program.cs.Place these underneath the Main method in your Program.cs file. Les deux méthodes utilisent la syntaxe yield return pour produire une séquence lors de leur exécution.These two methods both utilize the yield return syntax to produce a sequence as they run. Le compilateur génère un objet qui implémente IEnumerable<T> et génère la séquence de chaînes au fur et à mesure.The compiler builds an object that implements IEnumerable<T> and generates the sequence of strings as they are requested.

Maintenant, utilisez ces méthodes d’itération pour créer le jeu de cartes.Now, use these iterator methods to create the deck of cards. Placez la requête LINQ dans la méthode MainYou'll place the LINQ query in our Main method. :Here's a look at it:

// Program.cs
static void Main(string[] args)
{
    var startingDeck = from s in Suits()
                       from r in Ranks()
                       select new { Suit = s, Rank = r };

    // Display each card that we've generated and placed in startingDeck in the console
    foreach (var card in startingDeck)
    {
        Console.WriteLine(card);
    }
}

Les clauses from multiples produisent un SelectMany, qui crée une séquence unique en combinant chaque élément de la première séquence avec chaque élément de la deuxième séquence.The multiple from clauses produce a SelectMany, which creates a single sequence from combining each element in the first sequence with each element in the second sequence. L’ordre est important ici.The order is important for our purposes. Le premier élément de la première séquence source (couleurs) est associé à chacun des éléments de la deuxième séquence (rangs).The first element in the first source sequence (Suits) is combined with every element in the second sequence (Ranks). Cette opération génère les treize cartes de la première couleur.This produces all thirteen cards of first suit. Ce processus est reproduit pour chaque élément de la première séquence (couleurs).That process is repeated with each element in the first sequence (Suits). Le résultat final est un jeu de cartes classé par couleurs, puis par valeurs.The end result is a deck of cards ordered by suits, followed by values.

Il est important de garder à l’esprit la chose suivante : que vous choisissiez d’écrire votre code LINQ dans la syntaxe de requête utilisée plus haut ou d’utiliser plutôt la syntaxe de méthode, vous pourrez toujours passer d’une forme syntaxique à l’autre.It's important to keep in mind that whether you choose to write your LINQ in the query syntax used above or use method syntax instead, it's always possible to go from one form of syntax to the other. La requête ci-dessus, écrite dans la syntaxe de requête, s’écrit ainsi dans la syntaxe de méthode :The above query written in query syntax can be written in method syntax as:

var startingDeck = Suits().SelectMany(suit => Ranks().Select(rank => new { Suit = suit, Rank = rank }));

Le compilateur traduit les instructions LINQ écrites avec la syntaxe de requête dans la syntaxe d’appel de méthode équivalente.The compiler translates LINQ statements written with query syntax into the equivalent method call syntax. Par conséquent, les deux versions de la requête produisent le même résultat quel que soit le choix de syntaxe.Therefore, regardless of your syntax choice, the two versions of the query produce the same result. Choisissez la plus adaptée à votre situation : par exemple, si certains membres de votre équipe ont des difficultés à utiliser la syntaxe de méthode, privilégiez dans la mesure du possible la syntaxe de requête.Choose which syntax works best for your situation: for instance, if you're working in a team where some of the members have difficulty with method syntax, try to prefer using query syntax.

Ensuite, exécutez l’exemple que vous avez commencé à élaborer.Go ahead and run the sample you've built at this point. Il affiche les 52 cartes du jeu.It will display all 52 cards in the deck. Il peut être très utile d’exécuter cet exemple avec un débogueur pour observer la façon dont les méthodes Suits() et Ranks() s’exécutent.You may find it very helpful to run this sample under a debugger to observe how the Suits() and Ranks() methods execute. Vous pouvez clairement voir que chaque chaîne de chaque séquence est générée uniquement au moment requis.You can clearly see that each string in each sequence is generated only as it is needed.

Fenêtre console montrant l’application produisant 52 cartes.

Manipulation de l’ordreManipulating the Order

Maintenant, réfléchissons à la façon dont nous allons battre les cartes du jeu.Next, focus on how you're going to shuffle the cards in the deck. Pour bien faire, la première étape consiste à couper le jeu en deux.The first step in any good shuffle is to split the deck in two. Les méthodes Take et Skip, qui font partie des API LINQ, offrent cette fonctionnalité.The Take and Skip methods that are part of the LINQ APIs provide that feature for you. Placez-les sous la boucle foreach :Place them underneath the foreach loop:

// Program.cs
public static void Main(string[] args)
{
    var startingDeck = from s in Suits()
                       from r in Ranks()
                       select new { Suit = s, Rank = r };

    foreach (var c in startingDeck)
    {
        Console.WriteLine(c);
    }

    // 52 cards in a deck, so 52 / 2 = 26
    var top = startingDeck.Take(26);
    var bottom = startingDeck.Skip(26);
}

Cependant, il n’y a pas de méthode de battage dans la bibliothèque standard ; vous devez donc écrire la vôtre.However, there's no shuffle method to take advantage of in the standard library, so you'll have to write your own. Comme elle illustrera plusieurs techniques des programmes LINQ, nous allons expliquer les différentes parties du processus étape par étape.The shuffle method you'll be creating illustrates several techniques that you'll use with LINQ-based programs, so each part of this process will be explained in steps.

Pour ajouter des fonctionnalités aux interactions possibles avec les IEnumerable<T> obtenus à partir des requêtes LINQ, vous allez écrire des méthodes d’un genre particulier, nommées méthodes d’extension.In order to add some functionality to how you interact with the IEnumerable<T> you'll get back from LINQ queries, you'll need to write some special kinds of methods called extension methods. En bref, une méthode d’extension est une méthode statique spéciale qui ajoute de nouvelles fonctionnalités à un type existant sans qu’il soit nécessaire de modifier le type d’origine.Briefly, an extension method is a special purpose static method that adds new functionality to an already-existing type without having to modify the original type you want to add functionality to.

Pour accueillir vos méthodes d’extension, ajoutez à votre programme un nouveau fichier de classe statique, nommé Extensions.cs, puis créez la première méthode d’extension :Give your extension methods a new home by adding a new static class file to your program called Extensions.cs, and then start building out the first extension method:

// Extensions.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqFaroShuffle
{
    public static class Extensions
    {
        public static IEnumerable<T> InterleaveSequenceWith<T>(this IEnumerable<T> first, IEnumerable<T> second)
        {
            // Your implementation will go here soon enough
        }
    }
}

Examinez attentivement la signature de méthode, et en particulier les paramètres :Look at the method signature for a moment, specifically the parameters:

public static IEnumerable<T> InterleaveSequenceWith<T> (this IEnumerable<T> first, IEnumerable<T> second)

On peut voir l’ajout du modificateur this au premier argument de la méthode.You can see the addition of the this modifier on the first argument to the method. Cela signifie que vous appelez la méthode comme s’il s’agissait d’une méthode membre du type du premier argument.That means you call the method as though it were a member method of the type of the first argument. Cette déclaration de méthode suit également un idiome standard selon lequel les types d’entrée et de sortie sont IEnumerable<T>.This method declaration also follows a standard idiom where the input and output types are IEnumerable<T>. Cette pratique permet d’enchaîner les méthodes LINQ afin d’exécuter des requêtes plus complexes.That practice enables LINQ methods to be chained together to perform more complex queries.

Bien entendu, le jeu ayant été coupé en deux, il faut réunir les deux moitiés.Naturally, since you split the deck into halves, you'll need to join those halves together. Dans le code, cela signifie énumérer d’un seul coup les deux séquences acquises avec Take et Skip, en intercalant ( interleaving ) les éléments pour créer une seule séquence : le jeu de cartes battu.In code, this means you'll be enumerating both of the sequences you acquired through Take and Skip at once, interleaving the elements, and creating one sequence: your now-shuffled deck of cards. Pour écrire une méthode LINQ qui fonctionne avec deux séquences, vous devez comprendre comment IEnumerable<T> fonctionne.Writing a LINQ method that works with two sequences requires that you understand how IEnumerable<T> works.

L’interface IEnumerable<T> possède une seule méthode : GetEnumerator.The IEnumerable<T> interface has one method: GetEnumerator. L’objet retourné par GetEnumerator a une méthode permettant d’atteindre l’élément suivant et une propriété qui récupère l’élément actif dans la séquence.The object returned by GetEnumerator has a method to move to the next element, and a property that retrieves the current element in the sequence. Vous allez utiliser ces deux membres pour énumérer la collection et retourner les éléments.You will use those two members to enumerate the collection and return the elements. Cette méthode Interleave sera une méthode d’itération ; par conséquent, au lieu de créer une collection et de la retourner, vous allez utiliser la syntaxe yield return présentée ci-dessus.This Interleave method will be an iterator method, so instead of building a collection and returning the collection, you'll use the yield return syntax shown above.

Voici l’implémentation de cette méthode :Here's the implementation of that method:

public static IEnumerable<T> InterleaveSequenceWith<T>
    (this IEnumerable<T> first, IEnumerable<T> second)
{
    var firstIter = first.GetEnumerator();
    var secondIter = second.GetEnumerator();

    while (firstIter.MoveNext() && secondIter.MoveNext())
    {
        yield return firstIter.Current;
        yield return secondIter.Current;
    }
}

Maintenant que vous avez écrit cette méthode, revenez à la méthode Main et mélangez le jeu une fois :Now that you've written this method, go back to the Main method and shuffle the deck once:

// Program.cs
public static void Main(string[] args)
{
    var startingDeck = from s in Suits()
                       from r in Ranks()
                       select new { Suit = s, Rank = r };

    foreach (var c in startingDeck)
    {
        Console.WriteLine(c);
    }

    var top = startingDeck.Take(26);
    var bottom = startingDeck.Skip(26);
    var shuffle = top.InterleaveSequenceWith(bottom);

    foreach (var c in shuffle)
    {
        Console.WriteLine(c);
    }
}

ComparaisonsComparisons

Combien de battages faut-il compter pour remettre le jeu dans l’ordre d’origine ?How many shuffles it takes to set the deck back to its original order? Pour le savoir, il faut écrire une méthode qui détermine si deux séquences sont égales.To find out, you'll need to write a method that determines if two sequences are equal. Ensuite, il vous faudra placer le code qui mélange le jeu dans une boucle et vérifier si le jeu est de nouveau dans l’ordre.After you have that method, you'll need to place the code that shuffles the deck in a loop, and check to see when the deck is back in order.

Vous ne devriez pas avoir de problèmes à écrire une méthode qui détermine si deux séquences sont égales.Writing a method to determine if the two sequences are equal should be straightforward. La structure est similaire à celle de la méthode que vous avez écrite pour mélanger le jeu.It's a similar structure to the method you wrote to shuffle the deck. Seulement, cette fois, au lieu d’utiliser l’instruction yield return sur chaque élément, vous allez comparer les éléments correspondants de chaque séquence.Only this time, instead of yield returning each element, you'll compare the matching elements of each sequence. Une fois que la séquence aura été énumérée en entier, si tous les éléments correspondent, les séquences sont les mêmes :When the entire sequence has been enumerated, if every element matches, the sequences are the same:

public static bool SequenceEquals<T>
    (this IEnumerable<T> first, IEnumerable<T> second)
{
    var firstIter = first.GetEnumerator();
    var secondIter = second.GetEnumerator();

    while (firstIter.MoveNext() && secondIter.MoveNext())
    {
        if (!firstIter.Current.Equals(secondIter.Current))
        {
            return false;
        }
    }

    return true;
}

Cet exemple montre un autre terme LINQ : les méthodes terminales.This shows a second LINQ idiom: terminal methods. Elles prennent une séquence en entrée (ou, dans ce cas, deux séquences) et retournent une valeur scalaire unique.They take a sequence as input (or in this case, two sequences), and return a single scalar value. Les méthodes terminales sont toujours la dernière méthode de la chaîne de méthodes d’une requête LINQ ; d’où le nom « terminal ».When using terminal methods, they are always the final method in a chain of methods for a LINQ query, hence the name "terminal".

Vous pourrez les voir en action lorsque vous les utiliserez pour déterminer si le jeu est dans l’ordre d’origine.You can see this in action when you use it to determine when the deck is back in its original order. Placez le code de mélange à l’intérieur d’une boucle, et arrêtez-la lorsque la séquence est à nouveau dans l’ordre d’origine en appliquant la méthode SequenceEquals().Put the shuffle code inside a loop, and stop when the sequence is back in its original order by applying the SequenceEquals() method. Vous pouvez voir qu’il s’agit toujours de la dernière méthode d’une requête, parce qu’elle retourne une valeur unique plutôt qu’une séquence :You can see it would always be the final method in any query, because it returns a single value instead of a sequence:

// Program.cs
static void Main(string[] args)
{
    // Query for building the deck

    // Shuffling using InterleaveSequenceWith<T>();

    var times = 0;
    // We can re-use the shuffle variable from earlier, or you can make a new one
    shuffle = startingDeck;
    do
    {
        shuffle = shuffle.Take(26).InterleaveSequenceWith(shuffle.Skip(26));

        foreach (var card in shuffle)
        {
            Console.WriteLine(card);
        }
        Console.WriteLine();
        times++;

    } while (!startingDeck.SequenceEquals(shuffle));

    Console.WriteLine(times);
}

Exécutez le code ainsi obtenu et étudiez la façon dont le jeu se réorganise à chaque battage.Run the code you've got so far and take note of how the deck rearranges on each shuffle. Après huit battages (itérations de la boucle do-while), le jeu revient à la configuration qu’il avait à sa création avec la requête LINQ initiale.After 8 shuffles (iterations of the do-while loop), the deck returns to the original configuration it was in when you first created it from the starting LINQ query.

OptimisationsOptimizations

L’exemple que vous avez produit jusque-là exécute un mélange extérieur, où les cartes du haut et du bas restent les mêmes à chaque exécution.The sample you've built so far executes an out shuffle, where the top and bottom cards stay the same on each run. Faisons une modification en effectuant plutôt un mélange intérieur, où les 52 cartes changent toutes de position.Let's make one change: we'll use an in shuffle instead, where all 52 cards change position. Dans le cas d’un mélange intérieur, on intercale les deux moitiés du jeu de sorte que la première carte de la moitié du dessous devienne la première carte du jeu.For an in shuffle, you interleave the deck so that the first card in the bottom half becomes the first card in the deck. Cela signifie que la dernière carte de la moitié du dessus devient la carte du bas.That means the last card in the top half becomes the bottom card. Cette modification simple ne concerne qu’une ligne de code.This is a simple change to a singular line of code. Mettez à jour la requête de battage actuelle en inversant les positions de Take et de Skip,Update the current shuffle query by switching the positions of Take and Skip. de façon à modifier l’ordre des moitiés du dessus et du dessous du jeu :This will change the order of the top and bottom halves of the deck:

shuffle = shuffle.Skip(26).InterleaveSequenceWith(shuffle.Take(26));

Réexécutez le programme : vous verrez qu’il faut 52 itérations pour que le jeu se réorganise.Run the program again, and you'll see that it takes 52 iterations for the deck to reorder itself. Vous commencerez également à remarquer de sérieuses dégradations des performances lors de l’exécution du programme.You'll also start to notice some serious performance degradations as the program continues to run.

Il y a plusieurs raisons à cela.There are a number of reasons for this. L’une des causes principales de cette baisse de performances est une utilisation inefficace de l’évaluation paresseuse.You can tackle one of the major causes of this performance drop: inefficient use of lazy evaluation.

En bref, l’évaluation d’une instruction n’est effectuée que lorsque sa valeur devient nécessaire.Briefly, lazy evaluation states that the evaluation of a statement is not performed until its value is needed. Les requêtes LINQ sont évaluées de cette manière.LINQ queries are statements that are evaluated lazily. Les séquences sont générées uniquement au moment où les éléments sont demandés.The sequences are generated only as the elements are requested. En règle générale, c’est un avantage majeur de LINQ.Usually, that's a major benefit of LINQ. Toutefois, dans une utilisation du type de ce programme, cela entraîne une croissance exponentielle de la durée d’exécution.However, in a use such as this program, this causes exponential growth in execution time.

N’oubliez pas que nous avons généré le jeu d’origine à l’aide d’une requête LINQ.Remember that we generated the original deck using a LINQ query. Chaque mélange est généré en effectuant trois requêtes LINQ sur le jeu précédent.Each shuffle is generated by performing three LINQ queries on the previous deck. Toutes ces opérations sont effectuées de façon retardée.All these are performed lazily. Cela signifie également qu’elles sont effectuées à chaque fois que la séquence est demandée.That also means they are performed again each time the sequence is requested. À la 52e itération, vous aurez régénéré le jeu d’origine de nombreuses fois.By the time you get to the 52nd iteration, you're regenerating the original deck many, many times. Nous allons écrire dans un journal pour illustrer ce comportement.Let's write a log to demonstrate this behavior. Ensuite, vous résoudrez le problème.Then, you'll fix it.

Dans votre fichier Extensions.cs, tapez ou copiez la méthode d’extension ci-dessous.In your Extensions.cs file, type in or copy the method below. Elle crée un fichier nommé debug.log au sein de votre répertoire de projet et enregistre la requête en cours d’exécution dans le fichier journal.This extension method creates a new file called debug.log within your project directory and records what query is currently being executed to the log file. Elle peut être ajoutée à une requête pour marquer le fait qu’elle s’est exécutée.This extension method can be appended to any query to mark that the query executed.

public static IEnumerable<T> LogQuery<T>
    (this IEnumerable<T> sequence, string tag)
{
    // File.AppendText creates a new file if the file doesn't exist.
    using (var writer = File.AppendText("debug.log"))
    {
        writer.WriteLine($"Executing Query {tag}");
    }

    return sequence;
}

Vous verrez une ligne ondulée rouge sous File, ce qui signifie qu’elle n’existe pas.You will see a red squiggle under File, meaning it doesn't exist. Elle ne sera pas compilée, puisque le compilateur ne sait pas ce qu’est File.It won't compile, since the compiler doesn't know what File is. Pour résoudre ce problème, veillez à ajouter la ligne de code suivante sous la première ligne dans Extensions.cs :To solve this problem, make sure to add the following line of code under the very first line in Extensions.cs:

using System.IO;

Cela devrait résoudre le problème et faire disparaître l’erreur rouge.This should solve the issue and the red error disappears.

Instrumentez ensuite la définition de chaque requête avec un message de journalisation :Next, instrument the definition of each query with a log message:

// Program.cs
public static void Main(string[] args)
{
    var startingDeck = (from s in Suits().LogQuery("Suit Generation")
                        from r in Ranks().LogQuery("Rank Generation")
                        select new { Suit = s, Rank = r }).LogQuery("Starting Deck");

    foreach (var c in startingDeck)
    {
        Console.WriteLine(c);
    }

    Console.WriteLine();
    var times = 0;
    var shuffle = startingDeck;

    do
    {
        // Out shuffle
        /*
        shuffle = shuffle.Take(26)
            .LogQuery("Top Half")
            .InterleaveSequenceWith(shuffle.Skip(26)
            .LogQuery("Bottom Half"))
            .LogQuery("Shuffle");
        */

        // In shuffle
        shuffle = shuffle.Skip(26).LogQuery("Bottom Half")
                .InterleaveSequenceWith(shuffle.Take(26).LogQuery("Top Half"))
                .LogQuery("Shuffle");

        foreach (var c in shuffle)
        {
            Console.WriteLine(c);
        }

        times++;
        Console.WriteLine(times);
    } while (!startingDeck.SequenceEquals(shuffle));

    Console.WriteLine(times);
}

Remarquez que vous n’écrivez pas dans le journal à chaque fois que vous accédez à une requête,Notice that you don't log every time you access a query. mais seulement lors de la création de la requête d’origine.You log only when you create the original query. Le programme met toujours longtemps à s’exécuter, mais vous pouvez maintenant voir pourquoi.The program still takes a long time to run, but now you can see why. Si vous perdez patience au cours de l’exécution du mélange intérieur avec journalisation, revenez au mélange extérieur.If you run out of patience running the in shuffle with logging turned on, switch back to the out shuffle. Vous verrez quand même les effets de l’évaluation paresseuse.You'll still see the lazy evaluation effects. Sur une itération, elle exécute 2 592 requêtes, génération de toutes les valeurs et couleurs comprise.In one run, it executes 2592 queries, including all the value and suit generation.

Vous pouvez améliorer les performances du code en réduisant le nombre d’exécutions effectuées.You can improve the performance of the code here to reduce the number of executions you make. L’un des correctifs les plus simples consiste à mettre en cache les résultats de la requête LINQ d’origine qui construit le jeu de cartes.A simple fix you can make is to cache the results of the original LINQ query that constructs the deck of cards. Actuellement, les requêtes sont réexécutées chaque fois que la boucle do-while effectue une itération, construisant et battant de nouveau le jeu de cartes à chaque fois.Currently, you're executing the queries again and again every time the do-while loop goes through an iteration, re-constructing the deck of cards and reshuffling it every time. Pour mettre en cache le jeu de cartes, vous pouvez utiliser les méthodes LINQ ToArray et ToList ; ajoutées aux requêtes, elles effectuent les actions voulues, mais en stockant respectivement les résultats dans un tableau ou dans une liste.To cache the deck of cards, you can leverage the LINQ methods ToArray and ToList; when you append them to the queries, they'll perform the same actions you've told them to, but now they'll store the results in an array or a list, depending on which method you choose to call. Ajoutez la méthode LINQ ToArray aux deux requêtes et réexécutez le programme :Append the LINQ method ToArray to both queries and run the program again:

public static void Main(string[] args)
{
    var startingDeck = (from s in Suits().LogQuery("Suit Generation")
                        from r in Ranks().LogQuery("Value Generation")
                        select new { Suit = s, Rank = r })
                        .LogQuery("Starting Deck")
                        .ToArray();

    foreach (var c in startingDeck)
    {
        Console.WriteLine(c);
    }

    Console.WriteLine();

    var times = 0;
    var shuffle = startingDeck;

    do
    {
        /*
        shuffle = shuffle.Take(26)
            .LogQuery("Top Half")
            .InterleaveSequenceWith(shuffle.Skip(26).LogQuery("Bottom Half"))
            .LogQuery("Shuffle")
            .ToArray();
        */

        shuffle = shuffle.Skip(26)
            .LogQuery("Bottom Half")
            .InterleaveSequenceWith(shuffle.Take(26).LogQuery("Top Half"))
            .LogQuery("Shuffle")
            .ToArray();

        foreach (var c in shuffle)
        {
            Console.WriteLine(c);
        }

        times++;
        Console.WriteLine(times);
    } while (!startingDeck.SequenceEquals(shuffle));

    Console.WriteLine(times);
}

Le mélange extérieur est descendu à 30 requêtes.Now the out shuffle is down to 30 queries. Si vous repassez au mélange intérieur, vous constaterez des améliorations similaires : il ne comporte plus que 162 requêtes.Run again with the in shuffle and you'll see similar improvements: it now executes 162 queries.

Sachez que cet exemple est conçu pour mettre en évidence les cas d’usage où l’évaluation paresseuse cause des problèmes de performances.Please note that this example is designed to highlight the use cases where lazy evaluation can cause performance difficulties. S’il est important de voir son impact sur les performances du code, il faut également comprendre que toutes les requêtes ne doivent pas s’exécuter de manière stricte.While it's important to see where lazy evaluation can impact code performance, it's equally important to understand that not all queries should run eagerly. La baisse de performances que l’on constate sans ToArray est due au fait que chaque nouvelle disposition du jeu de cartes est construite à partir de la configuration précédente.The performance hit you incur without using ToArray is because each new arrangement of the deck of cards is built from the previous arrangement. Avec l’évaluation paresseuse, chaque nouvelle configuration du jeu est générée à partir du jeu d’origine, y compris l’exécution du code qui a construit startingDeck,Using lazy evaluation means each new deck configuration is built from the original deck, even executing the code that built the startingDeck. ce qui entraîne une grande quantité de travail supplémentaire.That causes a large amount of extra work.

Dans la pratique, certains algorithmes fonctionnent bien avec l’évaluation stricte, d’autres avec l’évaluation paresseuse.In practice, some algorithms run well using eager evaluation, and others run well using lazy evaluation. Au quotidien, l’évaluation paresseuse est en général un meilleur choix lorsque la source de données est un processus distinct, comme un moteur de base de données.For daily usage, lazy evaluation is usually a better choice when the data source is a separate process, like a database engine. L’évaluation paresseuse permet dans ce cas d’exécuter des requêtes plus complexes avec un seul aller-retour entre le processus de base de données et le reste du code.For databases, lazy evaluation allows more complex queries to execute only one round trip to the database process and back to the rest of your code. LINQ peut s’adapter tant à l’évaluation paresseuse qu’à l’évaluation stricte. Par conséquent, examinez vos processus et choisissez le type d’évaluation qui vous offre les meilleures performances.LINQ is flexible whether you choose to utilize lazy or eager evaluation, so measure your processes and pick whichever kind of evaluation gives you the best performance.

ConclusionConclusion

Dans ce projet, nous avons vu comment :In this project, you covered:

  • utiliser des requêtes LINQ pour agréger des données en une séquence explicite ;using LINQ queries to aggregate data into a meaningful sequence
  • écrire des méthodes d’extension pour ajouter des fonctionnalités personnalisées aux requêtes LINQ ;writing Extension methods to add our own custom functionality to LINQ queries
  • localiser les zones du code où les requêtes LINQ risquent de poser des problèmes de performances, comme une dégradation de la vitesse ;locating areas in our code where our LINQ queries might run into performance issues like degraded speed
  • utiliser l’évaluation paresseuse et l’évaluation stricte dans des requêtes LINQ, avec les implications que cela comporte sur les performances de requête.lazy and eager evaluation in regards to LINQ queries and the implications they might have on query performance

En dehors de LINQ, vous avez appris une technique de tour de cartes utilisée par les magiciens.Aside from LINQ, you learned a bit about a technique magicians use for card tricks. Les magiciens utilisent le mélange faro pour pouvoir contrôler le déplacement de chaque carte dans le jeu.Magicians use the Faro shuffle because they can control where every card moves in the deck. Maintenant que vous le savez, gardez le secret !Now that you know, don't spoil it for everyone else!

Pour plus d’informations sur LINQ, voir :For more information on LINQ, see: