3 ms·
I'm used to calling these "Generators", as in a "Generator Function". We could use Forth's defining words to build a pretty succinct version of this type of fun
by RodgerTheGreat 15y ago
I'm used to calling these "Generators", as in a "Generator Function". We could use Forth's defining words to build a pretty succinct version of this type of functionality. Generators will be called by executing their address ("xt" in Forth parlance), and will leave a flag on the stack indicating whether the generator had another element available. If this flag is true, the element produced will be underneath.
To start with, let's create a word called "count" which produces a generator which will count from 1 to infinity:
: count ( -- 'gen )
noname create 0 , latestxt
does> ( -- value? flag )
dup @ 1+ dup >r swap ! r> true
;
Then we build another generator which slices the first N elements of another generator, so that we can do things without going into an infinite loop:
: slice ( 'gen count -- 'gen )
noname create , , latestxt
does> ( -- value? flag )
dup @ 0= if drop false exit then
dup dup @ 1- swap !
cell+ @ execute
;
Finally, let's implement "map", so that we can apply a word to every element produced by the generator:
: map ( 'gen 'func -- )
2>r
begin
i' execute
if i execute
else 2rdrop exit
then
again
;
Now the Forth stack gives us a nice, fluent way of chaining these building blocks together. Let's start with that infinite counter, slice off the first 10 elements and print them out by mapping them to the number printing word ".":
count 10 slice ' . map
And we get:
1 2 3 4 5 6 7 8 9 10
Spiffy, hunh?