繁体   English   中英

我可以使用带有动态lambda列表的lambda(没有宏)吗?

[英]Can I use lambda with an on-the-fly lambda list (without macros)?

我正在尝试创建一个函数来返回函数,其中包含动态生成的任意lambda列表。 我可以用宏来做,但我试图去宏 - 如果我已经得到了:

(defmacro make-canned-format-macro (template field-names)
  `(function (lambda ,field-names
               (apply #'format `(nil ,,template ,,@field-names)))))

我可以用它如下:

* (make-canned-format-macro "~A-powered ~A" (fuel device))

#<FUNCTION (LAMBDA (FUEL DEVICE)) {10067D975B}>
* (setf (fdefinition 'zoom-zoom) (make-canned-format-macro "~A-powered ~A" (fuel device)))

#<FUNCTION (LAMBDA (FUEL DEVICE)) {1006835A5B}>
* (zoom-zoom "nuclear" "pogo stick")

"nuclear-powered pogo stick"

这正是我想要的行为。 它返回一个函数,其lambda列表是动态提供的(在这种情况下, (fuel device) 。)我正在尝试执行Proper Lisp Refactoring Thing并取消不必是宏的宏。 但是,我试图将一个任意的lambda列表变成一个正在函数中执行的lambda

* (defun make-canned-format (template field-names)
    #'(lambda field-names (apply #'format `(nil ,template ,@field-names))))
; in: DEFUN MAKE-CANNED-FORMAT
;     #'(LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))
; 
; caught ERROR:
;   The lambda expression has a missing or non-list lambda list:
;     (LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))

;     (SB-INT:NAMED-LAMBDA MAKE-CANNED-FORMAT
;         (TEMPLATE FIELD-NAMES)
;       (BLOCK MAKE-CANNED-FORMAT
;         #'(LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))))
; 
; caught STYLE-WARNING:
;   The variable TEMPLATE is defined but never used.
; 
; caught STYLE-WARNING:
;   The variable FIELD-NAMES is defined but never used.
; 
; compilation unit finished
;   caught 1 ERROR condition
;   caught 2 STYLE-WARNING conditions

MAKE-CANNED-FORMAT

我正在努力做甚么可能吗? (除了一些可怕的eval hack,它的可读性低于宏,我的意思是。)

要打开make-canned-format到一个函数,你需要更换functioncompile(coerce (lambda ...) 'function)

但是,您的重构是错误的。 make-canned-format 应该是一个宏 - 这样它将在当前的编译环境中产生一个闭包。 但是,该函数将在全局环境中产生闭包。

首先,你的宏过于复杂,如果你事先知道将使用多少个参数,你不需要发出一个调用来应用和构建一个中间参数列表。 这是另一个版本:

(defmacro lambda-format ((&rest args) template)
  `(lambda ,args (format nil ,template ,@args)))

您可以通过使用可变参数函数和APPLY去除宏,但这意味着通过仅检查生成的函数(使用例如inspect或describe),您无法预先知道需要多少参数:

(defun curry-format (template)
  (lambda (&rest args)
    (apply #'format nil template args)))

在格式化的情况下,您可以使用FORMATTER宏,它能够解析模板格式并在运行时为其提供参数之前发出警告:

(defmacro template ((&rest args) template)
  (let ((format-fn (gensym))
        (template-fn (copy-symbol :template)))
    `(let ((,format-fn (formatter ,template)))
       (flet ((,template-fn ,args (funcall ,format-fn nil ,@args)))
         (function ,template-fn)))))

这里我使用FLET,以便生成的函数具有用户友好的名称,但您也可以使用lambda。

(template (a b) "~x ~b")
#<FUNCTION (FLET "TEMPLATE") {1002B93D0B}>

如果你在上面调用describe,你可能会看到签名是准确的:

Lambda-list: (A B)

可变参数不是这种情况。

宏需要一个文字字符串,并且可以在宏展开期间检查它是否包含有效格式:

(template (a b) "~x ~!")
 ;; error in FORMAT: Unknown directive (character: EXCLAMATION_MARK)
 ;;  ~x ~!

给定如何指定FORMATTER ,如果实际参数的数量与预期参数的数量不同,则不会发出警告。 如果给定的参数太少,则在运行时会出现错误,如果给定的参数太多,则会将未使用的参数列表作为返回值(该列表也可以检查以给出错误)。

(defun make-canned-format (template field-names)
   #'(lambda field-names (apply #'format `(nil ,template ,@field-names))))

这是不可能的。 在lambda表达式中,参数列表是一个列表,而不是随后评估的任意符号。 Common Lisp需要一个固定的参数列表 - 而不是变量。 此评估表未评估:

(lambda (a b c)    ; (a b c) this is a list of parameters.
                   ;  This list is not evaluated.
  ...)

(lambda foo    ; foo is not allowed syntax. Common Lisp expects a list.
  ...)

LAMBDA使用普通的lambda列表

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM