Lisp 变量在定义中使用自身

Lisp variable using itself in definition

我正在使用 LTK 库在 Lisp 中构建一个 window 应用程序。我想要一个执行操作并可能隐藏自身的按钮。但是,这段代码:

(let* ((left (button 0 0 f "←" #'(lambda ()
                                   (decf start page-length)
                                   (funcall redraw)
                                   (if (>= start page-length)
                                       (ltk:configure left :state :visible))
                                       (ltk:configure left :state :hidden))))))

声称“left”是一个未定义的变量(其余的在代码中定义超出了这个问题的范围)。

最坏的情况是,我避免使用我编写的“按钮”功能并针对这种特定情况重新编写代码,但这种情况需要一个通用的解决方案。 Lisp有没有办法在变量的定义中使用函数中的变量?

只有一个绑定的 let*let 绑定相同。在执行正文之前,let 绑定不存在。在执行 button 期间,对 left 的引用必须来自较早的闭包或全局,因为 left 是在计算表达式后创建的。你可以这样做:

(let ((left nil))
  (setf left (button 0 0 f "←" #'(lambda ()
                                   (decf start page-length)
                                   (funcall redraw)
                                   (if (>= start page-length)
                                       (ltk:configure left :state :visible)
                                       (ltk:configure left :state :hidden))))))

注意:if 中存在错误,导致 lambda 始终执行 (ltk:configure left :state :hidden)

这里的价值是 CL 中 letrec 的一个版本:

(defmacro letrec (bindings &body decls/forms)
  (assert (and (listp bindings)
               (every (lambda (b)
                        (or (symbolp b)
                            (and (consp b)
                                 (symbolp (first b))
                                 (null (cddr b)))))
                      bindings))
      (bindings) "malformed bindings")
  (multiple-value-bind (names values)
      (loop for b in bindings
            collect (etypecase b
                      (symbol b)
                      (cons (first b)))
            into vars
            collect (etypecase b
                      (symbol nil)
                      (cons (second b)))
            into vals
            finally (return (values vars vals)))
      `(let ,names
         (psetf ,@(loop for name in names
                       for val in values
                       collect name
                       collect val))
         (locally
           ,@decls/forms))))

然后

> (letrec ((x (lambda (y)
                (if (null y)
                    'done
                  (funcall x (cdr y))))))
    (funcall x '(1 2 3)))
done