【问题标题】:Defining and using functions in variables in Common Lisp在 Common Lisp 中定义和使用变量中的函数
【发布时间】:2017-04-20 12:00:06
【问题描述】:

我偶然发现了 this 解释 Y Combinator 的文章。代码在 Scheme,但我正在尝试使用 Common Lisp 完成它。

但是,我在从 Scheme 到 Common Lisp 的翻译中遇到了麻烦。 Scheme 对函数和(其他)变量使用一个命名空间,但 Common Lisp 对函数和变量使用不同的命名空间。我怎样才能解决这个差异,以获得有效的 Common Lisp 代码?

方案代码

这是教程中的一些 Scheme 代码。

一开始作者定义了阶乘函数:

(define (factorial n)
  if (= n 0)
    1
    (* n (factorial (- n 1)))))

并将其翻译成这样:

(define factorial
  (lambda (n)
    (if (= n 0)
      1
      (* n (factorial (- n 1))))))

因为(根据作者)这就是 Scheme 所做的:

方案只是将第一个定义转换为第二个定义 在评估它之前。所以 Scheme 中的所有函数都是真正的 lambda 表达式。

普通的 Lisp

我尝试在 Common Lisp 中重写上述两个 sn-ps 以模仿从第一种形式到第二种形式的转换。但是 CL 中没有 define,也没有单一的名称空间。所以我试图用自己的方式作弊。

用 Common Lisp 重写第一个 Scheme 定义很容易:

(defun factorial (n)
    (if (= n 0)
        1
        (* n (factorial (- n 1)))))

但是(对我而言)将其转换为第二个定义有点棘手。我是这样翻译的:

(setf (symbol-function 'factorial)
  (lambda (n)
    (if (= n 0)
      1
      (* n (factorial (- n 1))))))

这是一种不好的方法(或者有更好的方法)?它似乎有效,但编译器给了我一个样式警告:未定义函数:阶乘。

【问题讨论】:

    标签: lambda scheme common-lisp


    【解决方案1】:

    如果我理解正确,您问题的主要内容是在"Lisp-1" and a "Lisp-2" 之间进行翻译。

    Scheme 是一个“Lisp-1”——它有一个用于函数和变量的命名空间。另一方面,Common Lisp 是“Lisp-2”——它为函数和变量提供单独的命名空间。

    在scheme中,可以写

    (define foo (lambda (...) ...))
    

    然后调用foo 喜欢:

    (foo ...)
    

    我们也可以在 Common Lisp 中以完全相同的方式定义 foo,但是如果我们尝试使用该语法调用 foo,您的程序将会崩溃。这是因为 foo 位于 variable 命名空间中,而不是 function 命名空间中。

    我们可以通过使用funcall 调用foo 来解决这个问题:

    (funcall foo ...)
    

    这是一个简短的介绍。 Functions 上的 Common Lisp Cookbook 页面提供了您可能会觉得有用的其他详细信息。

    【讨论】:

    • 是的,不同的名称空间方法是问题所在。我必须决定是将函数存储在值单元格中(并使用funcall 调用它)还是使用 Rainer 的解决方案(我想我会尝试)。 OP 中显示的转换只是一系列转换中的第一个,最终将产生 ` (define most-factorial (lambda (f) (lambda (n) (if (= n 0) 1 (* n (f ( - n 1))))))) (定义 Y (lambda (f) ((lambda (x) (xx)) (lambda (x) (f (xx)))))) (定义阶乘 (Y 几乎-阶乘))`。
    • @Frank,对。 Y 组合器涉及将函数作为参数传递并应用该函数。所以我认为无论你如何到达那里,你都需要funcall
    【解决方案2】:

    在某些方面更像是这样:

    (setf (symbol-function 'factorial)
          (labels ((factorial (n)
                     (if (= n 0)
                         1
                       (* n (factorial (- n 1))))))
            #'factorial))
    

    LABELS 定义了局部函数factorial。在本地函数factorial 的定义中,对factorial 的任何调用都是对该函数的调用。然后我们从标签表达式中返回这个函数。因此,您可以定义递归函数,其中递归调用不是对未定义函数的调用。

    如果您查看 Common Lisp 实现,您会发现 DEFUN 通常会扩展为不可移植的结构,例如命名的 lambda 函数。另外 DEFUN 也有编译时的副作用。

    【讨论】:

      【解决方案3】:

      这种转换在 Common List 中毫无意义。

      CL defun 通常不仅仅只是 (setf fdefinition)

      您可以通过评估 (macroexpand-1 '(defun foo (a b c) (bar c a b))) 来看到这一点。

      真正的问题是——你为什么要这样做?

      【讨论】:

      • 我只是自己学习更好地键入代码,而不是仅仅阅读它。由于 Scheme 和 Common Lisp 之间的差异,在这种情况下,这可能不是最好的主意。
      • 我不喜欢诸如“你为什么要这样做”之类的反问问题,尤其是因为弗兰克从他的动机开始,更好地理解 Y 组合子。我敢打赌,你没有费心去查他所指的那篇文章。问候,阿尔伯特
      猜你喜欢
      • 1970-01-01
      • 2019-09-29
      • 1970-01-01
      • 2017-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多