1

最近在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。但是,我对他们在做什么感到很困惑。我猜想开括号处的堆栈深度以某种方式隐藏在返回堆栈中。但是,我不明白。

这些对返回堆栈有什么作用?

在 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 之间的转换有关?

4

2 回答 2

4

2>r(以及 Forth 200x 字n>r)保留推入返回堆栈的元素的顺序。因此,如果您( 1 0 )在数据堆栈上有 0 作为堆栈的顶部,那么之后2>r您将在返回堆栈的顶部有 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 在该代码中所做的就是将一个值滑入返回堆栈的顶部以下。如果他(只是将深度推到返回堆栈的顶部,那么在退出这个单词时,gforth 会尝试跳转到深度作为地址。如果您尝试以这种方式使用他的话,则会看到相同的错误情况:

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

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

这段代码有几个陷阱:

  1. 可以说,返回堆栈的正常居民不能保证只占用返回堆栈的一个单元格。

  2. 的使用j依赖于关于j从中提取的返回堆栈的精确深度的知识——即,它依赖于关于如何DO ... LOOP实现和相关单词的知识。

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

于 2015-01-29T16:54:33.443 回答
0

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

将代码替换为等效代码

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

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

或者,您可以将其设为机器代码定义,然后无需担心返回地址。

CODE (
   <DEPTH>  <to-r>
ENDCODE

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

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

: (  POSTPONE DEPTH  POSTPONE >R  ;

我忽略了1+。是一种技术性,因为depth本身会将深度更改为1。因此,无论何时实际使用depth,您总是必须明智地添加1-1+

于 2019-07-20T12:24:16.043 回答