公共Lisp中的参数化返回

公共Lisp中的参数化返回,lisp,common-lisp,eval,block,Lisp,Common Lisp,Eval,Block,我正在学习Common lisp中的块,并通过此示例了解块和命令返回的工作原理: (block b1 (print 1) (print 2) (print 3) (block b2 (print 4) (print 5) (return-from b1)

我正在学习Common lisp中的块,并通过此示例了解块和命令返回的工作原理:

 (block b1 
            (print 1)
            (print 2)
            (print 3)
            (block b2 
                   (print 4)
                   (print 5)
                   (return-from b1)
                   (print 6)

               )
            (print 7))
它将按预期打印1、2、3、4和5。将return from更改为(return from b2),它将打印1、2、3、4、5和7,正如人们所期望的那样

然后,我尝试将其转换为函数,并对返回的标签进行paremetrize,从:

 (defun test-block (arg)  (block b1 
            (print 1)
            (print 2)
            (print 3)
            (block b2 
                   (print 4)
                   (print 5)
                   (return-from (eval arg))
                   (print 6)

               )
            (print 7)))

并使用(试块'b1)检查其是否有效,但不起作用。有没有一种没有条件的方法可以做到这一点?

我认为这类事情可以归结为Common Lisp中不同类型的名称空间绑定和环境

第一点是,一个稍有经验的学习Lisp的新手可能会尝试修改您尝试的函数,改为说
(eval(list'return from,arg))
。这似乎更有道理,但仍然不起作用

名称空间 在类似于语言的scheme中,一个常见的初学者错误是使用了一个名为
list
的变量,因为这会影响作为函数的顶级定义,并阻止程序员在此绑定范围内创建列表。Common Lisp中相应的错误是试图将符号作为函数使用,而它仅作为变量绑定

在公共Lisp中,有一些名称空间是从名称到事物的映射。有些名称空间是:

  • 功能。要获取相应的东西,可以调用它:
    (fooabc…
    ,或者获取静态符号的函数
    (function foo)
    (aka
    #'foo
    )或动态符号的函数
    (fdefinition'foo)
    。函数名是
    setf
    和一个符号(例如
    (插入条)
    )的符号或列表。符号也可以绑定到此命名空间中的宏,在这种情况下,
    function
    fdefinition
    会发出信号错误
  • 变量。这会将符号映射到相应变量中的值。这也将符号映射到常量。通过将变量写入
    foo
    或动态写入
    (符号值)
    来获取变量的值。符号也可以绑定为符号宏,在这种情况下,将应用特殊的宏扩展规则
  • 围棋牌。这会将符号映射到标签上,用户可以
    go
    (如其他语言中的
    goto
  • 街区。这会将符号映射到可以返回的位置
  • 捕获标签。这会将对象映射到捕捉对象的位置。当您将
    抛出
    到一个对象时,实现会有效地查找该命名空间中相应的
    捕获
    ,并将堆栈展开到该对象
  • 类(以及结构、条件)。每个类都有一个作为符号的名称(因此不同的包可能有一个
    类)
  • 包裹。每个包都由一个字符串命名,可能还有一些昵称。此字符串通常是符号的名称,因此通常为大写
  • 类型。每种类型都有一个作为符号的名称。当然,类定义也定义了类型
  • 声明。引入了
    声明
    声明
    宣告
  • 可能还有更多。这些都是我能想到的
catch标记和声明名称空间与其他名称空间不同,因为它们并不真正将符号映射到对象,但它们确实以下面描述的方式具有绑定和环境(请注意,我使用声明来指代已声明的内容,如优化策略或哪些变量是特殊的,而不是名称空间,例如,
优化
特殊
,实际上,
声明
活动,似乎太小而无法包含)

现在,让我们讨论这种映射可能发生的不同方式

名称与名称空间中某个事物的绑定是它们之间的关联方式,特别是它的形成方式和检查方式

绑定的环境是绑定存在的地方。它表示绑定存在的时间以及可以从何处访问绑定。搜索环境以查找与某个命名空间中的某个名称关联的内容

静态和动态绑定 如果绑定的名称在源代码中是固定的,那么我们说绑定是静态的;如果名称可以在运行时确定,那么绑定是动态的。例如
let
block
tagbody
中的标记都引入了静态绑定,而
catch
progv
引入了动态绑定

请注意,我对动态绑定的定义与规范中的定义不同。规范定义对应于下面我的动态环境

顶层环境 这是最后搜索名称的环境,也是顶级定义的所在,例如
defvar
defun
defclass
在此级别上运行。这是在搜索完所有其他适用环境后最后查找名称的环境,例如,如果函数或变量绑定无法执行在更接近的级别上找到,然后搜索此级别。有时可以在定义此级别的绑定之前对此级别的绑定进行引用,尽管它们可能会发出警告。也就是说,您可以定义一个函数
bar
,该函数在定义foo之前调用
foo
。在其他情况下,例如,您可以在定义包
foo
之前,不要尝试实习或读取符号
foo::bar
。许多命名空间只允许在顶级环境中进行绑定。这些

  • 常量(在变量名称空间内)
  • 班级
  • 包裹
  • 类型
尽管(除了
宣告
)所有绑定都是静态的,但通过调用
eval
可以有效地使它们成为动态的,该函数在顶层评估表单

函数(和[编译器]mac)
(defun foo ()
  (let ((x 10))
    (bar (lambda () x))))
(defun bar (f)
  (let ((x 20))
    (funcall f)))
(defun baz (islast)
  (let ((x (if islast 10 20)))
    (let ((lx (lambda () x)))
      (if islast
        lx
        (frob lx (baz t))))))
(defun frob (a b)
  (list (funcall a) (funcall b)))
(defun test-block (arg)
  (block b1 
    (print 1)
    (print 2)
    (print 3)
    (block b2 
      (print 4)
      (print 5)
      (case arg
        (b1 (return-from b1))
        (b2 (return-from b2)))
      (print 6))
    (print 7)))