2>r 和 2r> 是如何工作的?

How do 2>r and 2r> work?

最近在comp.lang.forth上发现了一些代码,好心的Coos Haak写的,我看不懂。

应该是对括号之间的数字求和或相乘。例如,

( 1 2 3 +)  ok
. 6  ok

为方便起见,我将其复制在这里:

: ( 
   depth 1+ r> 2>r 
; 

: cond 
   depth j > 
; 

: done 
   2r> rdrop 2>r 
; 

: +) 
   begin   cond 
   while   + 
   repeat 
   done 
; 

: *) 
   begin   cond 
   while   * 
   repeat 
   done 
; 

我看到了短语 r> 2>r2r> rdrop 2>r。但是,我对他们在做什么感到很困惑。我猜测左括号处的堆栈深度以某种方式隐藏在 return 堆栈中。但是,我不明白。

这些对 return 堆栈有什么作用?

在 Gforth 文档中我看到:

r>        R:w – w        core           “r-from”
2>r       d – R:d        core-ext       “two-to-r”
2r>       R:d – d        core-ext       “two-r-from”
rdrop     R:w –          gforth         “rdrop”

w  Cell, can contain an integer or an address 
d  double sized signed integer

这跟w和d的转换有关系吗?

2>r(以及 Forth 200x 词 n>r)保留推入 return 堆栈的元素的顺序。因此,如果数据堆栈上有 ( 1 0 ),堆栈顶部为 0,那么在 2>r 之后,return 堆栈顶部将有 0,其下方有 1 . 2>r 因此是可定义的,而不是

: 2>r  ]] >r >r [[ ; immediate

但是作为:

: 2>r  ]] swap >r >r [[ ; immediate

这些定义是等价的:

: a  ]] 0 >r 1 >r [[ ; immediate
: b  ]] 0 1 2>r [[ ; immediate

Coos Haak 在该代码中所做的是将一个值滑动到 return 堆栈顶部下方。如果他的 ( 只是将深度推到 return 堆栈的顶部,那么在退出这个词时,gforth 将尝试跳转到深度作为地址。如果您尝试以这种方式使用他的话,则会看到相同的错误情况:

: numbers  ( 1 2 ; 
: sum  +) ;
numbers sum
\ output: :16: error: Invalid memory address
\         >>>numbers<<< sum

如果 (+) 与 return 堆栈上的第三个元素而不是第二个元素协调,则该代码将起作用(并且正常使用会失败)。

这段代码有一些缺陷:

  1. 可以说,return 栈中的正常居民不能保证只占据 return 栈中的一个单元格。

  2. j 的使用依赖于对 j 从中提取的 return 堆栈的精确深度的了解 - 即,它依赖于了解如何DO ... LOOP和相关词实现。

这些词可以作为直接词可移植地实现,它们将在 return 堆栈的顶部保持深度,但是你不能在定义之外使用它们。它很简单,可以让它们像在任何给定的 Forth 上一样工作。

这是一个典型的过早优化的例子。 2>R 将两个项目移动到 return 堆栈,但标准规定了两个项目到达那里的顺序。 Coos Haak 知道这一点并接受了 "advantage"。

用等效的代码替换代码

 : ( 
   R>    \ remember return address
   depth >R
   >R    \ restore return address.
;

现在你明白是怎么回事了。你想记住堆栈深度,但如果它在堆栈上就会干扰计算。所以你把它放在 ( 代码的 return 地址下,稍后以类似的方式检索。

或者,您可以将其设为机器代码定义,这样就没有 return 地址可担心了。

CODE (
   <DEPTH>  <to-r>
ENDCODE

实际的机器代码留作练习。

另一种选择是使用宏,也不必担心 return 堆栈。

: (  POSTPONE DEPTH  POSTPONE >R  ;

我忽略了 1+ 。是一种技术性,因为 depth 本身将 depth 改变 1。所以你总是必须明智地添加 1-'s 或 1+'s 每当你实际使用 depth .