2

我在 Lisp 中弄湿了我的脚,defpackage并开始了一个可耻的开始,即一个我无法开始理解的错误。

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

我的代码的“肉”是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)

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

? (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 中。

4

2 回答 2

7

短篇故事

&是包中的一个内部符号。

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

这同样适用于^

(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::&)

附言

请注意,我使用 uninterned 符号作为:exportin - 的参数,defpackage以便它们不会被CL-USERin read

于 2016-08-25T16:29:50.240 回答
4

关于代码

我建议使用标准的 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 中的函数。将某些东西映射到其他东西的函数。Yourmapf是一个宏,它扩展为一个 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.

于 2016-08-25T18:14:05.143 回答