Question

This code trips me up. In this code the takeWhile makes sense - take up debits, reducing the balance while the balance is positive. The problem is with this code nothing happens. No calls to balance.debit() are made and I guess the reason is takeWhile filtering happens lazily.

I can ensure that it gets called by tacking on a toList() call, but that seems wasteful as a list is not really needed.

  • What is a better way to do this?
  • Is takeWhile not the right tool for this job?

Code follows:

class Balance {
  Balance(this._balance);
  double _balance;
  debit(double amount) {
    print('Debiting $amount');
    return (_balance -= amount);
  }
}

main() {
  final balance = new Balance(3.0);
  final debits = [ 1.0, 2.0, 3.0 ];
  debits.takeWhile((debit) => balance.debit(debit) > 0);

  //////////////////////
  //debits
  //  .takeWhile((debit) => balance.debit(debit) > 0)
  //  .toList();
}
Was it helpful?

Solution

No, takeWhile is not the right tool for this job.

As you noticed, it is lazy. It is intended to create a new iterable, and it only does something if you actually use that iterable. Here, you are not interested in the result at all, so you just want the "while" part, and don't care about the "take". Alternative:

for (debit in debits) {
  if (balance.debit(debit) < 0) break;
}

I even find it easier to read.

OTHER TIPS

This behaviour is described in the api doc of Iterable.takeWhile :

The filtering happens lazily.

To enforce evaluation you can use .length, .forEach((_){}), .toList() (...) on the result of takeWhile(...)

You are right, this is because it is lazy evaluated. You need something that enforces iterating over the result. If you don't want to create a list you could also do something like:

var r = debits.takeWhile((debit) => balance.debit(debit) > 0);
for(var x in r) {
  // print(x);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top