Macros 通用Lisp宏让curry-不工作
我发现自己调用了很多方法,这些方法的第一个参数是来自给定类的复杂对象。 虽然使用插槽和访问器很有用,但通用方法不能以这种方式绑定。所以我想:如果我们可以在本地curry任何函数,插槽+访问器+泛型函数+函数都可以用相同的结构来处理 我要清理的代码示例:Macros 通用Lisp宏让curry-不工作,macros,common-lisp,Macros,Common Lisp,我发现自己调用了很多方法,这些方法的第一个参数是来自给定类的复杂对象。 虽然使用插槽和访问器很有用,但通用方法不能以这种方式绑定。所以我想:如果我们可以在本地curry任何函数,插槽+访问器+泛型函数+函数都可以用相同的结构来处理 我要清理的代码示例: (defun clox-string (scanner) "Parse string into a token and add it to tokens" (loop while (and (char/= #\" (peek scanne
(defun clox-string (scanner)
"Parse string into a token and add it to tokens"
(loop while (and (char/= #\" (peek scanner))
(not (at-end-p scanner)))
do
(if (char= #\Newline (peek scanner)) (incf (line scanner))
(advance scanner)))
(when (at-end-p scanner)
(clox.error::clox-error (line scanner) "Unterminated string.")
(return-from clox-string nil))
(advance scanner) ;; consume closing "
(add-token scanner 'STRING (subseq (source scanner)
(1+ (start scanner))
(1- (current scanner)))))
这会更干净(我在CL中模仿这一点,但我通常会得到比Java更详细、可读性更低的代码——特别是在大量使用此类时)。正如在CL方法中不属于类一样,您最终会反复声明此类参数。这样会更好一些:
(defun clox-string (scanner)
"Parse string into a token and add it to tokens"
(let-curry scanner (peek at-end-p line source start current advance add-token)
(loop while (and (char/= #\" (peek))
(not (at-end-p)))
do
(if (char= #\Newline (peek)) (incf (line))
(advance)))
(when (at-end-p)
(clox.error::clox-error (line) "Unterminated string.")
(return-from clox-string nil))
(advance) ;; consume closing "
(add-token 'STRING (subseq (source)
(1+ (start))
(1- (current)))))
宏的草图(不工作):
编辑(添加):请注意,scanner
是一个类;start、source、line等,具有相同名称的插槽的访问器;将标记添加到具有多个参数的泛型函数中,推进具有一个参数的泛型方法:
(defclass scanner ()
((source
:initarg :source
:accessor source)
...
(...)))
(defmethod advance ((scanner scanner)) ...)
(defmethod add-token ((scanner scanner) token-type) ...)
更简单的错误示例:
;; With
(defun add (x y) (+ x y))
(defun mul (x y) (* x y))
;; I want to have this:
(let-curry 1000 (add mul)
(print (add 3))
(print (mul 3)))
;; expanding to:
(flet ((add (y) (add 1000 y))
(mul (y) (mul 1000 y)))
(print (add 3))
(print (mul 3)))
;; but instead I'm getting:
Execution of a form compiled with errors.
Form:
(FLET (LOOP
FOR
#1=#:G777
IN
(ADD MUL
)
COLLECT
(LIST #1#
(&REST ARGS)
(FUNCALL #1# 1000 ARGS)))
(PRINT (ADD 3))
(PRINT (MUL 3)))
Compile-time error:
The FLET definition spec LOOP is malformed.
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
谢谢!基本问题是:有没有可能使这样的宏工作?您的版本没有扩展到您想要的,但是:
(flet (loop for #:g8307 in (add mul) collect (list #:g8307 (&rest args) (funcall #:g8307 1000 args)))
(print (add 3)) (print (mul 3)))
现在需要在宏扩展时执行循环。
以下是一个工作版本:
(defmacro let-curry (obj (&rest functions) &body body)
"Locally curry all functions"
`(flet ,(loop for fn in functions
collect `(,fn (&rest args)
(apply #',fn ,obj args)))
,@body))
;; test it using add and mul from OP
(macroexpand-1 '(let-curry 10 (add mul) (list (add 5) (mul 5))))
;; ==>
(flet ((add (&rest args) (apply #'add 10 args))
(mul (&rest args) (apply #'mul 10 args)))
(list (add 5) (mul 5)))
(let-curry 10 (add mul) (list (add 5) (mul 5)))
;; ==> (15 50)
- 只有当您有遮挡/碰撞某物的危险,或者为了确保计算顺序最不令人惊讶时,才需要使用
,但在您的情况下,您实际上希望使用咖喱版本遮挡原始名称,因此仅使用原始名称是有意义的李>gensym
- 如果要有多个参数,应使用
apply
- 由于知道函数位于函数名称空间中,因此需要调用
,而不是#symbol
李>symbol
- 我在原型中使用了
而不是(&rest函数)
,使用不当(不是列表)会导致编译时错误,而且更精确李>函数
-
绑定到本地函数<代码>编译时错误:绑定时违反了包COMMON-LISP上的锁定-在包COMMON-LISP-USER中作为本地函数。@MartinBuchmann谢谢。我已经更改了函数以避免出现这种情况,但是如果需要的话,有一些方法可以解锁并避免出现这种情况。这不在这个答案的范围之内:)这真是太好了!它起作用了!但是,我的新方法无法设置:s@Alberto一切都可以修复,但不是在同一个问题上。哈哈,我肯定会打开一个新方法,然后在这里发布链接,谢谢:)
(defmacro let-curry (obj (&rest functions) &body body)
"Locally curry all functions"
`(flet ,(loop for fn in functions
collect `(,fn (&rest args)
(apply #',fn ,obj args)))
,@body))
;; test it using add and mul from OP
(macroexpand-1 '(let-curry 10 (add mul) (list (add 5) (mul 5))))
;; ==>
(flet ((add (&rest args) (apply #'add 10 args))
(mul (&rest args) (apply #'mul 10 args)))
(list (add 5) (mul 5)))
(let-curry 10 (add mul) (list (add 5) (mul 5)))
;; ==> (15 50)