Recursion and while loops in Factor
Tonight, I was willing to create a while
construct in Factor taking two items on the stack:
- a quotation to execute when the test is true
- a test quotation
For example, I wanted to be able to find the smallest power of two greater than or equal to an arbitrary number (here 34098) by doing:
1 [ 2 * ] [ dup 34098 < ] while
(of course there are much better ways of doing that, such as 34098 2 * 1 + 2 /i
but that’s not the point here)
This would be much easier if we could recurse from within a block (by using a recurse
word). Let’s start with that:
SYMBOL: recursive-block
: set-block ( quot -- )
recursive-block set ;
: recurse ( quot -- )
recursive-block get call ;
: with-recurse ( quot -- )
recursive-block get >r
dup set-block call
r> set-block ;
A quote passed through with-recurse
can use the recurse
word and re-execute itself.
Now that we have recursion, it is easy to implement while
using currying:
: while ( quot quot -- )
swap [ call recurse ] curry [ slip when ] 2curry
with-recurse ; inline
Note that inline
is used here to give the optimizing compiler a chance to build the complete quotation at compile-time if both quotations given to while
are statically known.
To create a vocabulary recursion
containing this code, one can create a file extra/recursion/recursion.factor
containing:
USING: kernel namespaces ;
IN: recursion
<PRIVATE
SYMBOL: recursive-block
: set-block ( quot -- )
recursive-block set ;
: init-block ( -- )
[ "recurse called without with-recurse" throw ] set-block ;
PRIVATE>
: recurse ( quot -- )
recursive-block get call ;
: with-recurse ( quot -- )
recursive-block get >r
dup set-block call
r> set-block ;
: while ( quot quot -- )
swap [ call recurse ] curry [ slip when ] 2curry
with-recurse ; inline
MAIN: init-block
Loading this library can be done by issuing the following command in the listener: "recursion" run
.
That’s all folks.