我正在尝试创建一组切曲奇的函数,并将它们放入一个散列中。到目前为止,我已经有了一个扩展为这样一个函数的宏:
(defmacro make-canned-format-macro (template field-names)
`(function (lambda ,field-names
(apply #'format `(nil ,,template ,,@field-names)))))
(defparameter *cookie-cutter-functions* (make-hash-table))
(setf (gethash 'zoom-zoom *cookie-cutter-functions*)
(make-canned-format-macro "~A-powered ~A" (fuel device)))
(setf (gethash 'delicious *cookie-cutter-functions*)
(make-canned-format-macro "~A ice cream" (flavor)))
(setf (gethash 'movie-ad *cookie-cutter-functions*)
(make-canned-format-macro "~A: A ~A and ~A film" (title star co-star)))重复的setf、gethash、make-canned-format-macro模式非常复杂,所以我尝试将其转换为循环:
(loop
for template in '(('zoom-zoom "~A-powered ~A" (fuel device))
('delicious "~A ice cream" (flavor))
('thematic "~A: A ~A and ~A film" (title star co-star)))
do (let ((func-name (car template))
(format-string (cadr template))
(params (caddr template)))
(setf (gethash func-name *cookie-cutter-functions*)
(make-canned-format-macro format-string params))))不幸的是,这是因为make-canned-format-macro操作的是值PARAMS,而不是params的值,因为它在编译时进行宏扩展,而不是在运行时进行评估。但是,正如我在询问this question时了解到的那样,make-canned-format-macro不会作为一个函数工作,因为它需要在编译时构造lambda表单。(至少,我想这就是我从中学到的?,请告诉我,这一点我错了!)我希望我的函数工厂是一个函数,而不是宏!)
我目前的想法是编写一个turn-this-list-of-templates-into-make-canned-format-macro-forms宏,而不是循环。这是应该做的事情(或者至少是一件非疯狂的事情),还是有更好的方法?
发布于 2018-09-26 08:03:39
由于您在编译/宏展开时知道参数,所以不需要应用:
CL-USER 35 > (defmacro make-canned-format-macro (template field-names)
`(function (lambda ,field-names
(format nil ,template ,@field-names))))
MAKE-CANNED-FORMAT-MACRO
CL-USER 36 > (macroexpand-1 '(make-canned-format-macro "~A-powered ~A" (fuel device)))
(FUNCTION (LAMBDA (FUEL DEVICE) (FORMAT NIL "~A-powered ~A" FUEL DEVICE)))
T也没有必要在一份清单中重复引用:
'('(a))这样的代码是非常不寻常的。
运行时代码生成
名称-macro没有任何意义,因为它构成了一个函数。函数需要生成可执行代码:要么使用EVAL,要么使用COMPILE。
CL-USER 56 > (defun make-canned-format-function (template field-names)
(compile nil `(lambda ,field-names
(format nil ,template ,@field-names))))
MAKE-CANNED-FORMAT-FUNCTION
CL-USER 57 > (loop
for (func-name format-string params)
in '((zoom-zoom "~A-powered ~A" (fuel device))
(delicious "~A ice cream" (flavor))
(thematic "~A: A ~A and ~A film" (title star co-star)))
do (setf (gethash func-name *cookie-cutter-functions*)
(make-canned-format-function format-string params)))
NIL基于宏的构建
CL-USER 77 > (defun make-canned-format-function-code (template fields)
`(lambda ,fields
(format nil ,template ,@fields)))
MAKE-CANNED-FORMAT-FUNCTION-CODE
CL-USER 78 > (defmacro def-canned-format-functions (ht description)
`(progn ,@(loop
for (func-name format-string params) in description
collect `(setf (gethash ',func-name ,ht)
,(make-canned-format-function-code format-string params)))))
DEF-CANNED-FORMAT-FUNCTIONS
CL-USER 79 > (pprint
(macroexpand-1
'(def-canned-format-functions
*foo*
((zoom-zoom "~A-powered ~A" (fuel device))
(delicious "~A ice cream" (flavor))
(thematic "~A: A ~A and ~A film" (title star co-star))))))
(PROGN
(SETF (GETHASH 'ZOOM-ZOOM *FOO*)
(LAMBDA (FUEL DEVICE)
(FORMAT NIL "~A-powered ~A" FUEL DEVICE)))
(SETF (GETHASH 'DELICIOUS *FOO*)
(LAMBDA (FLAVOR)
(FORMAT NIL "~A ice cream" FLAVOR)))
(SETF (GETHASH 'THEMATIC *FOO*)
(LAMBDA (TITLE STAR CO-STAR)
(FORMAT NIL "~A: A ~A and ~A film" TITLE STAR CO-STAR))))在您的代码中,您可以在顶层编写:
(def-canned-format-functions
*foo*
((zoom-zoom "~A-powered ~A" (fuel device))
(delicious "~A ice cream" (flavor))
(thematic "~A: A ~A and ~A film" (title star co-star))))发布于 2018-09-26 03:47:09
作为一个函数,你绝对可以做你想做的事。这将不是最漂亮的代码,但它会工作。从宏中提取的关键是正确的:它们是在编译time1中评估的--我看了一下您引用的另一个问题,当它们给您提供一些很好的优化/重构建议时,有时您只是想要您想要的东西。因此,我尝试将您的代码进行最小程度的更改,以使其只进行一次更改:与其使用在编译时为eval的宏,不如将其设置为生成代码的函数,然后在运行时执行该代码:
(defun make-canned-format (template field-names)
(eval `(lambda ,field-names
(apply #'format `(nil ,,template ,,@field-names)))))现在,您应该能够为函数的大量定义做任何有意义的事情(例如,在宏、循环中包装,什么的)--关于这种方法要记住的是,使用相同的模板/字段名重复调用的性能将很差(因为它在盲目地重新生成相同的源代码,并且在运行时对它进行逐次调用,而宏的定义在编译时只会执行一次)。但是,由于您似乎是每一对参数调用它一次,并存储生成的结果,这将不是一个问题。
1除非您使用这里使用的方法来生成宏并在运行时对其进行评估。这可能会让人感到困惑,甚至比宏更难调试,但是可以这样做。
https://stackoverflow.com/questions/52507680
复制相似问题