Gerry Jackson
2023-02-27 21:03:50 UTC
On Friday, January 29, 2016 at 7:19:28 AM UTC, humptydumpty wrote:
: YC ( .. xt xt |0 -- .. ) BEGIN WHILE execute REPEAT ;
and like the way it separates out the loop from the processing. However
LATESTXT is not a standard word and using YC is a bit messy.
A way around LATESTXT is to use a quotation, for example using HD's
DOWNCOUNT we can do:
: yc ( .. xt xt |0 -- .. ) BEGIN WHILE execute REPEAT ;
: (dc) ( n -- n-1 f ) dup . 1- dup 0 >= ;
0 value downcount-xt
: downcount
[: ['] (dc) execute if downcount-xt dup else 0 then ;]
dup dup to downcount-xt yc
;
10 downcount drop \ displays 10 9 8 7 6 5 4 3 2 1 0 ok
Still rather messy but shows the idea. Much of the body of DOWNCOUNT
would be common with other uses of YC and can be auto compiled so an
improved syntax is:
' (dc) iterator downcount
or of course
:noname dup . 1- dup 0 >= ; iterator downcount
where ITERATOR is a parsing word that compiles the equivalent of the
above code
A definition of ITERATOR using GForth's ]] ... [[ multi-postponer is:
: iterator ( "name" xt -- )
variable r@ >in !
: r> >in ! ' ( -- xt2 ) ( R: -- xt )
]] @ and ?dup [[ ( -- xt3 xt3|0 ) ( R: -- xt2 )
]] ;] dup dup [[
r> compile,
]] ! yc ; [[
;
1. A variable is used instead of a value as TO can't be postponed
2. downcount is parsed 3 times for the name of the variable, the name of
the iterating word and access to the variable
3. The stack diagram of the processing word e.g. (dc) is
(i*x -- j*y 0|-1), 0|-1 so that the IF statement can be replaced with
an AND
5 downcount drop 5 4 3 2 1 0 ok
The YC loops can be effectively nested and a processing pipeline formed
by code such as this simple example that replaces spaces in the input
stream with a '|' character. CATCH THROW are used simply to avoid the
verbose error messages Gforth displays when ABORT" is used.
: .word ( ca u -- 0|-1 )
dup if
2dup s" stop" compare 0=
if cr 99 throw then
type '|' emit
else
2drop
then
;
: (word) parse-name dup if .word -1 else 2drop cr 0 then ;
' (word) iterator next-word
: (line) refill if next-word -1 else 0 then ;
' (line) iterator next-line
: x cr ['] next-line catch 99 <> abort" Failed" ." Succeeded" ;
x
qwerty f hj asd qwerty|f|hj|asd|
'stop' will exit NEXT-LINE stop 'stop'|will|exit|NEXT-LINE|
Succeeded ok
I don't know why HD called it YC as it isn't a Y combinator but I don't
know what else is a good name.
: YC ( .. xt xt |0 -- .. ) BEGIN WHILE execute REPEAT ;
: xt latestxt postpone literal ; immediate
\ Use: : wordname .... test-sequence IF xt dup ELSE 0 THEN ;
\ Kick into action: ' wordname dup YC
: downcount dup . 1- dup 0 >= IF xt dup ELSE 0 THEN ;
10 ' downcount dup YC drop
---
*Factored* loop. Small. Beautiful. Deserves to be a primitive.
Enjoying forth,
humptydumpty
I've been experimenting with humptydumpty's (HD's) YC as defined above\ Use: : wordname .... test-sequence IF xt dup ELSE 0 THEN ;
\ Kick into action: ' wordname dup YC
: downcount dup . 1- dup 0 >= IF xt dup ELSE 0 THEN ;
10 ' downcount dup YC drop
---
*Factored* loop. Small. Beautiful. Deserves to be a primitive.
Enjoying forth,
humptydumpty
and like the way it separates out the loop from the processing. However
LATESTXT is not a standard word and using YC is a bit messy.
A way around LATESTXT is to use a quotation, for example using HD's
DOWNCOUNT we can do:
: yc ( .. xt xt |0 -- .. ) BEGIN WHILE execute REPEAT ;
: (dc) ( n -- n-1 f ) dup . 1- dup 0 >= ;
0 value downcount-xt
: downcount
[: ['] (dc) execute if downcount-xt dup else 0 then ;]
dup dup to downcount-xt yc
;
10 downcount drop \ displays 10 9 8 7 6 5 4 3 2 1 0 ok
Still rather messy but shows the idea. Much of the body of DOWNCOUNT
would be common with other uses of YC and can be auto compiled so an
improved syntax is:
' (dc) iterator downcount
or of course
:noname dup . 1- dup 0 >= ; iterator downcount
where ITERATOR is a parsing word that compiles the equivalent of the
above code
A definition of ITERATOR using GForth's ]] ... [[ multi-postponer is:
: iterator ( "name" xt -- )
variable r@ >in !
: r> >in ! ' ( -- xt2 ) ( R: -- xt )
r ]] [: [[ r> r> compile, ( R: -- ) \ compile xt
dup >r compile, \ compile xt2]] @ and ?dup [[ ( -- xt3 xt3|0 ) ( R: -- xt2 )
]] ;] dup dup [[
r> compile,
]] ! yc ; [[
;
1. A variable is used instead of a value as TO can't be postponed
2. downcount is parsed 3 times for the name of the variable, the name of
the iterating word and access to the variable
3. The stack diagram of the processing word e.g. (dc) is
(i*x -- j*y 0|-1), 0|-1 so that the IF statement can be replaced with
an AND
5 downcount drop 5 4 3 2 1 0 ok
The YC loops can be effectively nested and a processing pipeline formed
by code such as this simple example that replaces spaces in the input
stream with a '|' character. CATCH THROW are used simply to avoid the
verbose error messages Gforth displays when ABORT" is used.
: .word ( ca u -- 0|-1 )
dup if
2dup s" stop" compare 0=
if cr 99 throw then
type '|' emit
else
2drop
then
;
: (word) parse-name dup if .word -1 else 2drop cr 0 then ;
' (word) iterator next-word
: (line) refill if next-word -1 else 0 then ;
' (line) iterator next-line
: x cr ['] next-line catch 99 <> abort" Failed" ." Succeeded" ;
x
qwerty f hj asd qwerty|f|hj|asd|
'stop' will exit NEXT-LINE stop 'stop'|will|exit|NEXT-LINE|
Succeeded ok
I don't know why HD called it YC as it isn't a Y combinator but I don't
know what else is a good name.
--
Gerry
Gerry