Re: [v2] Custom for-loops: Enumerators, Iterators, Generators
Posted: 14 Mar 2023, 01:59
You all might be interested in this. A while back, I wrote a "chainable" iterator class that's kind of a functional programming approach. Since I saw you all were discussing this topic, I extricated this from my main code library:
It's layering on the functions for the ultimate call to __Enum from the for loop. You need an itb class for this to work, which would contain the usual iterable methods (map, reduce, etc). They rely on too much of my standard library to extricate them here, but they are all standard implementations. But the enm class above was the real breakthrough part for me.
With this you can chain a bunch of stuff (like a Java stream, or lazy evaluation) such that the items pass through one at a time, rather than making intermediate lists. Like generators instead of list comprehensions for the Python gurus in the thread. If you also DefineProp the itb methods into Array.Prototype, the top of a for loop that uses this could look something like:
for i in myArray.drop(5).filter(x=>x>50).take(10).transform(x=>x*100)
where this drops the first 5 items, filters those above 50 but only the next 10 of them, multiplying each by 100. This is a crazy example, but it demonstrates the power and terse nature of chained enumerator functions.
The itb.take method looks like this, for example, so you can see how the constructor gets called:
You can see how this then gets wrapped around the functions already inside the enm chain when __Enum is called.
Code: Select all
class enm extends itb { ;enumerator wrapper extends iterable
__New(ebl, enmw, atyx?) { ;instantiate w enum wrapper and arity transform
this.ebl := ebl ;enumerable subject
, this.enmw := enmw ;wrapper for __Enum(a)(p*)
, this.atyx := atyx ?? (x=>x) ;arity transform
}
__Enum(aty) ;enumeration start with arity
=> ((t:=this).aty := aty ;store arity in object
, t.enmw.call(t.ebl.__Enum(t.atyx.call(aty)))) ;call on wrapped fnc/arity
} ;=============================================================================
With this you can chain a bunch of stuff (like a Java stream, or lazy evaluation) such that the items pass through one at a time, rather than making intermediate lists. Like generators instead of list comprehensions for the Python gurus in the thread. If you also DefineProp the itb methods into Array.Prototype, the top of a for loop that uses this could look something like:
for i in myArray.drop(5).filter(x=>x>50).take(10).transform(x=>x*100)
where this drops the first 5 items, filters those above 50 but only the next 10 of them, multiplying each by 100. This is a crazy example, but it demonstrates the power and terse nature of chained enumerator functions.
The itb.take method looks like this, for example, so you can see how the constructor gets called:
Code: Select all
take(cnt) => enm(this ;enum take first count
, (i:=0, f => (p*) => (++i > cnt ? 0 : f(p*))))