为什么在我将代码打包到包中后,列表中只有两个元素不再被识别?

Why do just two elements in a list cease to be recognized after I wrap my code in a package?

我开始接触 Lisp 中的 defpackage 并开始了一个可耻的开始,即一个我无法开始理解的错误。

下面的代码试图创建一种子语言来对向量执行中缀操作。我想将其用于涉及一些线性代数的项目。

我代码的'meat'是parse-infix。该函数找到具有最高优先级的运算符,调用 apply-op 将所述运算符及其操作数替换为 operator (operand, operand) 从而缩小列表,并迭代直到列表仅包含结果。支持的运算符是四个规则、相等(将结果绑定到 Lisp 符号)和向量连接。

这是代码,完整无缺:

(defpackage :infix
       (:use :common-lisp)
   (:export operator-list
            operators
            parse-infix
            infix))

(in-package :infix)

(defun parse-input (a)
 "Turns symbols into numbers as necessary while reading an expression"
    (if (symbolp a) (symbol-value a) a))

;; Definition of structure containing data for one operator

(defmacro mapf (op type)
 ""
   `(lambda (a b) 
       (map ,type #'(lambda (x y)
                       (funcall ,op x y)) (parse-input a) (parse-input b))))

(defstruct (operator
              (:conc-name op-)
              (:constructor op (sym &key (func (mapf sym 'vector)) priority associativity n-operands)))
            sym                            ; Operator symbol
            func                           ; Function to be applied to operands
            priority                       ; Other operators attributes
            (associativity 'left-to-right) ; Evaluation order for operators
                                           ; that appear more than once in a row
            (n-operands 2))                ; Number of operands (NOT IMPLEMENTED)

(defmacro operator-list (&body ops)
 "Produces an operator list from a list of operator structs."
   `(mapcar #'(lambda (y) (apply #'op 
       (mapcar #'(lambda (x) (if (listp x) (eval x) x)) y))) ',ops))


(defparameter operators
  (operator-list 
     (+ :priority 4)
     (- :priority 4)
     (* :priority 3)
     (/ :priority 3)
     (^ :priority 2  :func expt :associativity right-to-left)
     (& :priority 2  :func (lambda (x y) (concatenate 'vector x y)))
     (= :priority 10 :func (lambda (x y) (set (intern (string x)) y))))
 "Default set of operators, which perform arithmetic operations on
  vectors lengthwise. If one vector is shorter than the other, the result
  is truncated.")

(defun apply-op (b)
 "Reads a segment of a list of the format operand/operator/operand (in 3 consecutive
  cells) and leaves operator (operand, operand) in a single cell."
   (setf (car b) (funcall (op-func (caadr b))
                          (car b)
                          (caddr b))
         (cdr b) (cdddr b)))

(defun parse-infix (b &key (operator-list operators))
 "Parses an infix expression by calling apply-op repeatedly until the entire
  expression is processed."
(let ((expr (mapcar #'(lambda (x)
                         (case (type-of x)
                                 (symbol (or (member x operator-list :key #'op-sym) x))
                                 (cons (parse-infix x))
                                 (otherwise x))) b)))
    (loop while (cdr expr) do
        (apply-op (reduce #'(lambda (x y &aux (op1 (caadr x)) (op2 (caadr y)))
                                       (if (or (< (op-priority op2) (op-priority op1))
                                           (and (= (op-priority op1) (op-priority op2))
                                                (eq (op-associativity op1) 'right-to-left))) y x))
                            (remove-if #'listp (mapcon #'list expr) :key #'caddr)))
     finally (return (car expr)))))

(defmacro infix (&rest b)
 "Wrapper to create lists for parse-infix"
   `(parse-infix ',b))

问题来了。这些功能似乎正在运行...

? (infix (#(2 3) + #(4 5)) * #(2 2))
#(12 16)
? (infix (#(100) & (#(2 3) + #(4 5)) * #(2 2))) ; '& is concatenation
#(200 12)
? (infix A = #(5 5) + #(10 10))
#(15 15)
? A
#(15 15)

...但是当我离开包时,连接 (&) 运算符突然 'dies':

? (in-package :cl-user)
#<Package "COMMON-LISP-USER">
? (infix:infix A = #(5 5) + #(10 10))
#(15 15)
? (infix:infix (#(2 3) + #(4 5)) * #(2 2))
#(12 16)
? (infix:infix (#(100) & (#(2 3) + #(4 5)) * #(2 2)))
> Error: The value & is not of the expected type LIST.
> While executing: (:INTERNAL INFIX:PARSE-INFIX), in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
1 >

我试图跟踪包的功能并注意到,无论出于何种原因,当我离开 infix 包时,'& 不再被识别为运算符。我不知道为什么会这样。欢迎任何意见。

PS。许多人可能已经注意到,所有这些都在 Clozure Common Lisp 中。

短篇小说

& 是您包中的内部符号。

您需要将其导出以解决问题。

^也是如此:

(defpackage :infix
  (:use :common-lisp)
  (:export #:operator-list
           #:operators
           #:parse-infix
           #:infix
           #:&
           #:^))

详情

当您键入时

(infix:infix (#(100) & (#(2 3) + #(4 5)) * #(2 2)))

cl-user 包中时, 符号 &+* 读作 cl-user::&cl:+cl:*。 请注意,后两者是从 common-lisp 包,因此 也可在您的 infix 包裹中使用:

(eq 'infix::+ 'cl-user::+)
==> T
(eq 'infix::+ 'cl-user::+)
==> T

但是第一个不一样:

(eq 'infix::& 'cl-user::&)
==> NIL

find-all-symbols 是你的 朋友:

(find-all-symbols "+")
==> (+)
(find-all-symbols "*")
==> (*)
(find-all-symbols "&")
==> (INFIX::& CL-USER::&)

PS

请注意,我使用非实习符号作为 :export 参数 在 defpackage - 这样他们就不会被 read.

实习到 CL-USER

关于代码.

我建议使用标准的 Lisp 缩进。

没有

(mapcar (lambda (a)
          a) b)

但是

(mapcar (lambda (a)
          a)
        b)

制作operator-listmapf宏没什么意义。普通功能会更好。但是:operator-list 可能是一个宏,它应该去掉 EVAL。在宏中使用 eval 是一种代码味道。 eval 通常是不需要的,甚至是错误的工具。

如果您想使 operator-list 成为一个宏,那么它应该扩展为代码而不是使用 eval。您可能希望它将表单扩展为类似以下内容:

(list (make-operator :sym 'foo :func (lambda (x y) ...))
      ...
      (make-operator :sym 'bar :func (lambda (x y) ...)))

您的代码将其扩展为 mapcar 的调用。这违背了定义宏的目的。

您的代码:

(defmacro mapf (op type)
  `(lambda (a b) 
     (map ,type
          #'(lambda (x y)
              (funcall ,op x y))
          (parse-input a)
          (parse-input b))))

#'(lambda (x y)
   (funcall ,op x y))

基本上就是#'op.

哪个会成功:

(defmacro mapf (op type)
  `(lambda (a b) 
     (map ,type
          #',op
          (parse-input a)
          (parse-input b))))

还有命名不好。 map-something 是 Lisp 中的函数。将某些东西映射到其他东西上的功能。你的 mapf 是一个宏,它扩展成一个 lambda 表达式,它本身什么都不做 - 直到被调用。

但是,实际上应该是这样的

您需要这样的运算符定义:

(defops operators
  (+ :priority 4)
  (- :priority 4)
  (* :priority 3)
  (/ :priority 3)
  (^ :priority 2  :func #'expt :associativity :right-to-left)
  (& :priority 2  :func (lambda (x y) (concatenate 'vector x y)))
  (= :priority 10 :func (lambda (x y) (set (intern (string x)) y))))

这就是宏:

(defmacro defops (symbol &body ops)
 "Produces an operator list from a list of operator structs."
 `(defparameter ,symbol
    (list ,@(loop for (op . args) in ops
                  collect `(make-operator :sym ',op ,@args)))))

这是它创建的代码:

(DEFPARAMETER OPERATORS
  (LIST (MAKE-OPERATOR :SYM '+ :PRIORITY 4)
        (MAKE-OPERATOR :SYM '- :PRIORITY 4)
        (MAKE-OPERATOR :SYM '* :PRIORITY 3)
        (MAKE-OPERATOR :SYM '/ :PRIORITY 3)
        (MAKE-OPERATOR :SYM '^ :PRIORITY 2
                       :FUNC #'EXPT
                       :ASSOCIATIVITY :RIGHT-TO-LEFT)
        (MAKE-OPERATOR :SYM '& :PRIORITY 2
                       :FUNC (LAMBDA (X Y)
                               (CONCATENATE 'VECTOR X Y)))
        (MAKE-OPERATOR :SYM '= :PRIORITY 10
                       :FUNC (LAMBDA (X Y)
                               (SET (INTERN (STRING X)) Y)))))

如您所见,不需要EVAL