处理参数传递给Common Lisp中的defmacro

Pau*_*des 0 macros lambda common-lisp

在尝试解决Project Euler中的问题时,我编写了以下函数和宏:

(defun digits (n &key (base 10)) ;; Returns a list with the digits of 'n'
   (if (< n base) (list n)       ;; in a given base.
      (multiple-value-bind (div rem)
         (floor n base)
         (concatenate 'list (digits div :base base) (list rem)))))

(defmacro test-palindromes (n1 n2)
   (let* ((dn1 (digits n1)) (dn2 (digits n2))
          (hash (loop for i in dn1 ; A-list describing digit associations
                      collecting (assoc i (pairlis dn2
                            (loop for i from 0 below (length dn1)
                                  collecting i))))))
      `(lambda (n1 n2) (and ,@(loop for i in hash
                                    for j from 0
                                    collecting `(char= (char n1 ,(cdr i)) (char n2 ,j)))))))
Run Code Online (Sandbox Code Playgroud)

我想要做的是生成一个lambda,T如果一个字符串对应另一个字符串的特定排列,则返回该lambda .例如:

1 > (pprint (macroexpand-1 '(test-palindromes 1089 9801)))

(LAMBDA (N1 N2)
  (AND (CHAR= (CHAR N1 3) (CHAR N2 0))
       (CHAR= (CHAR N1 2) (CHAR N2 1))
       (CHAR= (CHAR N1 1) (CHAR N2 2))
       (CHAR= (CHAR N1 0) (CHAR N2 3))))
1 >
Run Code Online (Sandbox Code Playgroud)

当输入是整数时,它工作正常...

1 > (funcall (test-palindromes 1089 9801) "ALAS" "SALA")
T
1 > (funcall (test-palindromes 1089 9801) "ALAS" "SALE")
Nil
Run Code Online (Sandbox Code Playgroud)

...但如果我尝试给它更复杂的输入,则会失败:

1 > (setf g 10)
10
1 > (funcall (test-palindromes 1089 (+ 9791 g)) "ALAS" "SALA")
> Error: The value (+ 9791 G) is not of the expected type REAL.
> While executing: CCL::<-2, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
2 >
Run Code Online (Sandbox Code Playgroud)

无奈之下,通过设定尝试了笨拙的解决方案dn1,并dn2(eval (digits dn1))(eval (digits dn2)).这产生了部分改善......

2 > (funcall (test-palindromes 1089 (+ 9791 g)) "ALAS" "SALA")
T
Run Code Online (Sandbox Code Playgroud)

...但是这段代码仍然会失败:

(loop for pos from 0 to 66
      nconcing (loop for i in (nthcdr (1+ pos) 4-digits)
                     for j from 0
                     when (equal (sort (digits (nth pos 4-digits)) #'<)
                                 (sort (digits i) #'<))
                       collect (test-palindromes (nth pos 4-digits) i)))
> Error: Unbound variable: POS
> While executing: CCL::CHEAP-EVAL-IN-ENVIRONMENT, in process listener(1).
> Type :GO to continue, :POP to abort, :R for a list of available restarts.
> If continued: Retry getting the value of POS.
> Type :? for other options.
3 >
Run Code Online (Sandbox Code Playgroud)

(该变量4-digits包含4位数的所有完美正方形的有序列表.)

我想有些东西应该eval被删除,但我并不真正理解循环中发生了什么.为什么pos不再被认可?这个循环可以工作吗?任何输入都表示赞赏.

谢谢,保罗

jla*_*ahd 5

defmacro当在REPL或文件中解释或编译代码时,执行正文中的代码.在你的最后一环test-palindromes,你给宏的说法(nth pos 4-digits)-但pos只有当有一个明智的意义loop执行.

因此,您尝试做的事情不能用宏来完成,因为宏的输出取决于执行时输入的值.

但你可以使用一个函数:

* (defun test-palindromes (n1 n2)
    (let* ((dn1 (digits (eval n1))) (dn2 (digits (eval n2)))
           (hash (loop for i in dn1
                       collecting (assoc i (pairlis dn2
                                                    (loop for i from 0 below (length dn1)
                                                          collecting i))))))
      (lambda (n1 n2)
        (loop for i in hash
              for j from 0
              always (char= (char n1 (cdr i)) (char n2 j))))))
TEST-PALINDROMES
* (test-palindromes 1089 9801)
#<COMPILED-LEXICAL-CLOSURE (:INTERNAL TEST-PALINDROMES) #x21012A08CF>
* (funcall * "ALAS" "SALA")
T
Run Code Online (Sandbox Code Playgroud)

如果您出于某种原因,以您在宏中执行的方式展开lambda的内容(即,而不是a loop,执行and包含char=比较的表单),您也可以使用函数执行此操作:

(defun test-palindromes (n1 n2)
  (let* ((dn1 (digits (eval n1))) (dn2 (digits (eval n2)))
         (hash (loop for i in dn1
                     collecting (assoc i (pairlis dn2
                                                  (loop for i from 0 below (length dn1)
                                                        collecting i))))))
    (eval `(lambda (n1 n2) 
             (and ,@(loop for i in hash
                          for j from 0
                          collecting `(char= (char n1 ,(cdr i)) (char n2 ,j))))))))
Run Code Online (Sandbox Code Playgroud)

但可能这种方法没用.