5

问题flet在于其中绑定的函数必须内联定义。换句话说,没有办法做到这一点:

(new-flet ((a (lambda (f x)
                (funcall f (* x 2))))
           (b (function-generator)))
    (a #'b 10))

我考虑自己定义这样一个宏,但问题是这flet似乎是设置局部函数值的唯一方法。symbol-function始终只获取全局定义,function不能与setf. 任何人都知道如何可以相当干净地完成这项工作,如果有的话?

4

4 回答 4

5

您可以轻松搭建蹦床

(defun function-generator (x)
  (lambda (y) (* x y)))

(let ((fg (function-generator 42)))
  (flet ((a (f x) (funcall f (* x 2)))
         (b (x) (funcall fg x)))
    (a #'b 10)))

new-flet使用这种方法的宏实现是

(defmacro new-flet (bindings &body body)
  (let ((let-bindings (list))
        (flet-bindings (list))
        (args (gensym)))
    (dolist (binding bindings)
      (let ((name (gensym)))
        (push `(,name ,(second binding))
              let-bindings)
        (push `(,(first binding) (&rest ,args)
                 (apply ,name ,args))
              flet-bindings)))
    `(let ,(nreverse let-bindings)
       (flet ,(nreverse flet-bindings)
         ,@body))))

在您的示例中扩展为

(macroexpand-1 '(new-flet ((a (lambda (f x) (funcall f (* x 2))))
                           (b (function-generator)))
                  (a #'b 10)))

==> (LET ((#:G605 (LAMBDA (F X)
                    (FUNCALL F (* X 2))))
          (#:G606 (FUNCTION-GENERATOR)))
      (FLET ((A (&REST #:G604)
               (APPLY #:G605 #:G604))
             (B (&REST #:G604)
               (APPLY #:G606 #:G604)))
        (A #'B 10)))
于 2013-08-21T16:35:03.693 回答
1

(let* ((a (lambda (f x) (funcall f (* x 2))))
       (b (function-generator)))
    (funcall a b 10))

一个相当干净的解决方案来解决您的问题?

于 2013-08-21T16:27:23.843 回答
1

如何将变量与let, 绑定,以便它们setf能够,然后使用 aflet作为主体,let以便它们也funcall能够和(function …)-able。例如,我给出了一个愚蠢的小功能而不是(generate-function)

(let ((a (lambda (f x)
           (funcall f (* x 2))))
      (b (lambda (&rest args)
           (print (list* 'print-from-b args)))))
  (flet ((a (&rest args)
           (apply a args))
         (b (&rest args)
           (apply b args)))
    (a #'b 10)))

我们可以相对容易地将其包装在一个宏中:

(defmacro let/flet (bindings &body body)
  (let ((args (gensym (string '#:args-))))
    `(let ,bindings
       (flet ,(loop :for (name nil) :in bindings
                 :collect `(,name (&rest ,args) (apply ,name ,args)))
         ,@body))))

现在

(let/flet ((a (lambda (f x)
                (funcall f (* x 2))))
           (b (lambda (&rest args)
                (print (list* 'print-from-b args)))))
  (a #'b 10))

展开为第一个代码块。请注意,您也可以(a b 10)在正文中使用,因为 的绑定b与 的值相同#'b。您也可以setf在变量上使用:

(let/flet ((a (lambda (x)
                (print (list 'from-a x)))))
  (a 23)
  (setf a (lambda (x)
            (print (list 'from-new-a x x))))
  (a 23))

印刷

(FROM-A 23) 
(FROM-NEW-A 23 23) 
于 2013-08-21T17:12:23.900 回答
0

如果有人对labels等价物感兴趣,这里是:

(defmacro my-labels ((&rest definitions) &rest body)
  (let ((gensyms (loop for d in definitions collect (gensym)))
        (names (loop for d in definitions collect (car d)))
        (fdefs (loop for f in definitions collect (cadr f)))
        (args (gensym)))
    `(let (,@(loop for g in gensyms collect (list g)))
       (labels (,@(loop for g in gensyms for n in names
                     collect `(,n (&rest ,args) (apply ,g ,args))))
         ,@(loop for g in gensyms for f in fdefs
                collect `(setf ,g ,f))
         ,@body))))

这有点像 Scheme 的letrec.

于 2013-08-26T11:04:41.577 回答