我如何在 Forth 中实现 Y 组合器?
How do I implement Y-combinator in Forth?
在 Rosetta Code Forth 中没有 Y 组合器的实现。
我该怎么做?我如何在 Forth 中使用 Y 组合器?为什么?
这是我对 Y 组合器的尝试。当您将 y
应用于 xt 时,您会得到另一个 xt。当你执行这个新的xt时,它会执行第一个xt并传入第二个xt。
\ Address of an xt.
variable 'xt
\ Make room for an xt.
: xt, ( -- ) here 'xt ! 1 cells allot ;
\ Store xt.
: !xt ( xt -- ) 'xt @ ! ;
\ Compile fetching the xt.
: @xt, ( -- ) 'xt @ postpone literal postpone @ ;
\ Compile the Y combinator.
: y, ( xt1 -- xt2 ) >r :noname @xt, r> compile, postpone ; ;
\ Make a new instance of the Y combinator.
: y ( xt1 -- xt2 ) xt, y, dup !xt ;
使用例如像这样:
\ Count down from 10; passed to the anonymous definition.
10
\ Anonymous definition which recursively counts down.
:noname ( u xt -- ) swap dup . 1- ?dup if swap execute else drop then ;
\ Apply the Y combinator and execute the result.
y execute
\ Should print 10 9 8 7 6 5 4 3 2 1.
至于为什么,没有实际原因。这是函数递归调用自身而无需显式命名函数的一种方式。但是(标准)Forth 有 RECURSE
,即使在 :NONAME
定义中也是如此。
构想
Y
组合词的定义原则上可以很短。例如,使用SP-Forth中的低级代码生成器词汇表,可以表示为:
: Y ( xt1 -- xt2 )
\ xt2 identifies the following semantics: "xt2 xt1 EXECUTE"
CONCEIVE GERM LIT, EXEC, BIRTH
;
而且由于体积小,所以很容易理解。
这里 CONCEIVE
开始一个单词定义,GERM
给出被定义单词的 xt,LIT,
推迟一个数字(从堆栈中),EXEC,
推迟执行(来自堆栈的 xt),BIRTH
完成定义并给出其 xt.
\ test
:NONAME ( u xt -- ) SWAP DUP IF 1- DUP . SWAP EXECUTE EXIT THEN 2DROP ;
5 SWAP Y EXECUTE
\ Should print 4 3 2 1 0
向 Standard Forth 迈出一步
不幸的是,在当前的 Forth 标准中没有办法得到一个被定义的词的 xt。因此,要以标准方式定义 Y
我们应该使用某种间接方式。没有 GERM
功能,之前的 Y
定义可以改写为:
: Y ( xt1 -- xt2 )
HERE 0 , >R \ allot one cell in data-space to keep xt2
CONCEIVE
R@ LIT, '@ EXEC, \ addr @
EXEC, \ xt1 CALL
BIRTH DUP R> ! \ store xt2 into allotted cell
;
标准 Forth 中的解决方案
并且只使用标准词它会变得稍微长一些:
: Y ( xt1 -- xt2 )
HERE 0 , >R >R \ allot one cell in data-space to keep xt2
:NONAME R> R@ ( xt1 addr )
POSTPONE LITERAL POSTPONE @ \ addr @
COMPILE, \ xt1 EXECUTE
POSTPONE ; DUP R> ! \ store xt2 into allotted cell
;
当然,没有理由在实际代码中使用 Y
字,因为 Forth 有 RECURSE
字用于直接递归。
在 Rosetta Code Forth 中没有 Y 组合器的实现。
我该怎么做?我如何在 Forth 中使用 Y 组合器?为什么?
这是我对 Y 组合器的尝试。当您将 y
应用于 xt 时,您会得到另一个 xt。当你执行这个新的xt时,它会执行第一个xt并传入第二个xt。
\ Address of an xt.
variable 'xt
\ Make room for an xt.
: xt, ( -- ) here 'xt ! 1 cells allot ;
\ Store xt.
: !xt ( xt -- ) 'xt @ ! ;
\ Compile fetching the xt.
: @xt, ( -- ) 'xt @ postpone literal postpone @ ;
\ Compile the Y combinator.
: y, ( xt1 -- xt2 ) >r :noname @xt, r> compile, postpone ; ;
\ Make a new instance of the Y combinator.
: y ( xt1 -- xt2 ) xt, y, dup !xt ;
使用例如像这样:
\ Count down from 10; passed to the anonymous definition.
10
\ Anonymous definition which recursively counts down.
:noname ( u xt -- ) swap dup . 1- ?dup if swap execute else drop then ;
\ Apply the Y combinator and execute the result.
y execute
\ Should print 10 9 8 7 6 5 4 3 2 1.
至于为什么,没有实际原因。这是函数递归调用自身而无需显式命名函数的一种方式。但是(标准)Forth 有 RECURSE
,即使在 :NONAME
定义中也是如此。
构想
Y
组合词的定义原则上可以很短。例如,使用SP-Forth中的低级代码生成器词汇表,可以表示为:
: Y ( xt1 -- xt2 )
\ xt2 identifies the following semantics: "xt2 xt1 EXECUTE"
CONCEIVE GERM LIT, EXEC, BIRTH
;
而且由于体积小,所以很容易理解。
这里 CONCEIVE
开始一个单词定义,GERM
给出被定义单词的 xt,LIT,
推迟一个数字(从堆栈中),EXEC,
推迟执行(来自堆栈的 xt),BIRTH
完成定义并给出其 xt.
\ test
:NONAME ( u xt -- ) SWAP DUP IF 1- DUP . SWAP EXECUTE EXIT THEN 2DROP ;
5 SWAP Y EXECUTE
\ Should print 4 3 2 1 0
向 Standard Forth 迈出一步
不幸的是,在当前的 Forth 标准中没有办法得到一个被定义的词的 xt。因此,要以标准方式定义 Y
我们应该使用某种间接方式。没有 GERM
功能,之前的 Y
定义可以改写为:
: Y ( xt1 -- xt2 )
HERE 0 , >R \ allot one cell in data-space to keep xt2
CONCEIVE
R@ LIT, '@ EXEC, \ addr @
EXEC, \ xt1 CALL
BIRTH DUP R> ! \ store xt2 into allotted cell
;
标准 Forth 中的解决方案
并且只使用标准词它会变得稍微长一些:
: Y ( xt1 -- xt2 )
HERE 0 , >R >R \ allot one cell in data-space to keep xt2
:NONAME R> R@ ( xt1 addr )
POSTPONE LITERAL POSTPONE @ \ addr @
COMPILE, \ xt1 EXECUTE
POSTPONE ; DUP R> ! \ store xt2 into allotted cell
;
当然,没有理由在实际代码中使用 Y
字,因为 Forth 有 RECURSE
字用于直接递归。