https://dart.dev/guides/language/language-tour#generators
When you need to lazily produce a sequence of values, consider using a generator function. Dart has built-in support for two kinds of generator functions:
As explained above, what exactly does "lazily" mean for a synchronous generator function? Is there a difference in behavior from a normal function?
void main(){
Iterable<int> list1 = naturalsTo(3);
print(list1.toList());//[0, 1, 2]
print(naturalsTo(3).toList());//[0, 1, 2]
}
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k ;
}
Iterable<int> naturalsTo2(int n){
int k=0;
List<int> resultList = [];
for(k;k<n;k ){
resultList.add(k);
}
return resultList;
}
The results of the above code both return the same list, but I'm not sure how they behave differently.
What exactly does "lazily" mean?
CodePudding user response:
Lazy evaluation is a general term to describe behavior where a result isn't computed until it's needed. It defers computation to avoid computing results that won't be used.
Your example (even after correcting it to not call the same function for both cases) does not demonstrate the difference. Instead consider a situation where:
- Each loop iteration has an observable side-effect. In the example below, we'll use
print
, but in practice that observable side-effect usually is that each loop iteration takes some measurable amount of time. - We don't need to iterate over all elements.
void main() {
print(naturalsTo(3).take(2).toList());
print(naturalsTo2(3).take(2).toList());
}
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) {
print(k);
yield k ;
}
}
Iterable<int> naturalsTo2(int n) {
int k = 0;
List<int> resultList = [];
for (k; k < n; k ) {
print(k);
resultList.add(k);
}
return resultList;
}
The first case (which uses the synchronous generator) prints:
0
1
[0, 1]
The second case (which uses a normal function) prints:
0
1
2
[0, 1]
CodePudding user response:
lazily means it calculates the values as you need them.
The thing is though, when you say toList()
you actually say, "I need them all now", so you lose all benefits of the lazy calculation.
To demonstrate how it works let's say this is the function, notice the print:
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) {
print(k);
yield k ;
}
}
Then execute this code:
var a = naturalsTo(10).toList();
print("=====");
var b = naturalsTo(10);
print("-----");
var c = b.elementAt(5);
you will see it generates this output:
0
1
2
3
4
5
6
7
8
9
=====
-----
0
1
2
3
4
5
So, just creating the iterable doesn't execute any of the code in it. converting it to a list generates all the values. And just requesting a single value from an iterable only generates the values up to that value.
This can be beneficial if the calculations take a long time to process or even when the iterable generates an infinite sequence. It's often used in Listviews so only the rows that are shown on screen are calculated.