Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit 5c109d4

Browse files
authored
2nd LINQ section: lazy evaluation (#40)
* first draft of the next LINQ page. * second page of LINQ is ready.
1 parent 1db2665 commit 5c109d4

File tree

4 files changed

+87
-0
lines changed

4 files changed

+87
-0
lines changed

LINQ/docs/lazy-evaluation.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Query evaluation is lazy
2+
3+
In LINQ, query evaluations are *lazy*. That means the output of a LINQ query isn't generated from the code that defines the query. Instead, it's generated when some code requests the results of the query. To illustrate this behavior, let's separate the code that generates an input sequence from the code that runs the sequence.
4+
5+
This first block of code generates an input sequence using the numbers 1 through 9:
6+
7+
``` cs --region generate-source-sequence --source-file ../src/Program.cs --project ../src/LINQ.csproj --session lazy-evaluation-basic
8+
```
9+
10+
This method is an *iterator method* and is explained in more detail below. For now, what's important is that it displays a message for each new item requested.
11+
12+
This next block of code consumes this sequence, using the LINQ query you say on the previous page:
13+
14+
``` cs --region consume-sequence --source-file ../src/Program.cs --project ../src/LINQ.csproj --session lazy-evaluation-basic
15+
```
16+
17+
Click the *Run* button to try it. Pay attention to the order of the messages.
18+
19+
First, notice that the messages generated from creating the sequence are interspersed with the messages to display the output sequence. But more than that, notice how two elements are generated for each output message. When an even element is generated, it doesn't pass the `where` clause. The query asks for the next input element. That one does pass the filter, so its square is computed and displayed.
20+
21+
Modify the code that generates the sequence, or the code that modifies the sequence to explore on your own. For a few examples, try the following:
22+
23+
- Modify the generator to create all the even numbers, then all the odd numbers:
24+
```csharp
25+
for (int i = 2; i < 10; i += 2)
26+
{
27+
Console.WriteLine($"\tProducing {i}");
28+
yield return i;
29+
}
30+
for (int i = 1; i < 10; i += 2)
31+
{
32+
Console.WriteLine($"\tProducing {i}");
33+
yield return i;
34+
}
35+
```
36+
- Modify the consumer to change which elements are filtered:
37+
```csharp
38+
where n % 2 == 0
39+
```
40+
- Turn that single query into multiple queries:
41+
```csharp
42+
var numbers = from n in GenerateSequence()
43+
select n;
44+
var oddNumbers = from p in numbers
45+
where p % 2 == 1
46+
select p;
47+
var squares = from s in oddNumbers
48+
select n * n;
49+
```
50+
- Add loops to display the output of each of those separate queries.
51+
52+
Try your own ideas.
53+
54+
The preceding suggestion shows two key advantages to the lazy evaluation used by LINQ. LINQ queries *compose* well. You can create queries or methods that return the results of queries. Those queries can be combined easily to perform very sophisticated algorithms. Combining smaller and simpler queries creates more readable code without sacrificing other design considerations.
55+
56+
The other advantage to lazy evaluation and composition is that each element of the input sequence is processed in turn, and each element of the output sequence is processed in turn. That means it's not necessary to story interim results in memory.

LINQ/docs/query-syntax.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ select n * n;
3838
Every query ends with a *projection* that produces either a new sequence, or a single value. This first example produces a sequence where every element is the square of the input sequence.
3939

4040
You can modify any of these sections in the query and run it yourself.
41+
42+
**Next: [Lazy evaluations &raquo;](./lazy-evaluation.md) Previous: [Home &laquo;](../README.md)**

LINQ/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ You can explore LINQ in this interactive tutorial. Each lesson teaches a new LIN
1212
## Lessons in this tutorial
1313

1414
- [LINQ Query Syntax](docs/query-syntax.md)
15+
- [Understand query evaluation](docs/lazy-evaluation.md)
1516
- More in upcoming PRs

LINQ/src/Program.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading.Tasks;
45

@@ -25,6 +26,8 @@ static int Main(
2526
return region switch
2627
{
2728
"query-syntax" => QuerySyntax(),
29+
"generate-source-sequence" => ConsumeSequence(),
30+
"consume-sequence" => ConsumeSequence(),
2831
_ => throw new ArgumentException("A --region argument must be passed", nameof(region))
2932
};
3033
}
@@ -43,5 +46,30 @@ internal static int QuerySyntax()
4346
return 0;
4447
}
4548

49+
internal static int ConsumeSequence()
50+
{
51+
#region consume-sequence
52+
var sequence = GenerateSequence();
53+
var squaresOfOddNumbers = from n in sequence
54+
where n % 2 == 1
55+
select n * n;
56+
57+
foreach (var number in squaresOfOddNumbers)
58+
Console.WriteLine(number);
59+
#endregion
60+
return 0;
61+
}
62+
63+
#region generate-source-sequence
64+
internal static IEnumerable<int> GenerateSequence()
65+
{
66+
for (int i = 1; i < 10; i++)
67+
{
68+
Console.WriteLine($"\tProducing {i}");
69+
yield return i;
70+
}
71+
}
72+
#endregion
73+
4674
}
4775
}

0 commit comments

Comments
 (0)