理论上可以用标签来重写tagbody吗?

问题描述 投票:0回答:1

我一直在思考如何在不提供显式 goto 的 Lisp 中实现 Common Lisp 的

tagbody
,但提供
labels

我对此感兴趣的原因是我想编写一个递归解释器,支持 goto 语句,其工作方式与 common lisp 中完全相同。

假设有

labels
可用,是否可以将
tagbody
实现为用
labels
编写的宏?

考虑这个用

tagbody
编写的示例:

(tagbody
        (go middle)

    start
        (print "start")
        (go end)

    middle
        (print "middle")
        (go start)

    end
        (print "end"))

现在考虑用

labels
编写的另一个示例:

(labels
    ((entry-point () (middle))
     (start () (print "start") (end))
     (middle () (print "middle") (start))
     (end () (print "end")))
    (entry-point))

表面上他们似乎做着同样的事情。有谁知道他们是否真的这样做?它们在语义上真的 100% 等效吗?

如果有人编写了一个将一种形式转换为另一种形式的宏,是否会出现一些边缘情况,其中细微的差异可能会重新出现?例如相同输入的不同输出,或微妙的变量范围差异,或堆栈展开的差异等。

lisp common-lisp
1个回答
0
投票

如果您假设尾部调用变成 GO TO(这意味着您的代码将不可移植 CL),那么这在理论上是可能的,但在实践中可能非常困难。

实践中困难的原因是你需要处理动态状态,这将变成一场噩梦。例如考虑

(setf *x* 0)
(tagbody
 start
 (if (> *x* 0)
     (go end)
     (let ((*x* 1))
       (go start)))
 end)

其中

*x*
是一个特殊变量。这无法终止。但是

(setf *x* 0)
(labels ((start ()
          (if (> *x* 0)
              (end)
              (let ((*x* 1))
                (start))))
         (end ()))
  (start))

将在相同的假设下终止。

语言中还有许多其他动态状态,例如 catch 标记、异常处理程序以及您需要处理的许多其他内容。

从另一个角度思考这个问题会更容易:给定一个看起来像尾调用的函数的调用,您需要知道动态状态是什么,才能知道它是否真的尾调用。

go
本质上迫使“调用”成为尾部调用,因此必须愿意解除在
go
与其目标之间建立的任何动态状态。 (我认为在 CL 中,在这种情况下你永远不需要安装动态状态位。)

© www.soinside.com 2019 - 2024. All rights reserved.