如何使用函数(或闭包)对象设置本地函数定义?

时间:2013-08-21 15:48:28

标签: lisp common-lisp

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

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

我考虑过自己定义这样一个宏,但问题是flet似乎是唯一的方式来设置本地函数值。 symbol-function始终仅获取全局定义,function不能与setf一起使用。如果有的话,任何人都知道如何干净利落地完成这项工作?

4 个答案:

答案 0 :(得分: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)))

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

如何将变量与let绑定,以便它们setf能够,然后使用flet作为let的正文,以便它们'能够funcall能够和(function …)能够。例如,我给了一个愚蠢的小函数而不是(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) 

答案 3 :(得分: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