2016-08-25 13 views
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中。

回答

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:+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

請注意,我用uninterned符號作爲:export參數 在defpackage - ,使它們不被read拘留到CL-USER

+2

它也許值得檢查符號的名稱,而不是實際的符號標識。這就是**循環**所做的。例如,你可以做'(循環:用於列表中的x)'或'(循環#:用於列表中的x)「等。 –

+0

賓果!現在正在工作。雖然採取了稍微不同的方法。我將符號轉換爲字符串以利用'(string'infix:&)==(string'&)' –

+0

您也可以使用字符。 – sds

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中的函數。映射其他東西的函數。您的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

+0

感謝您的全面審查。事實上,'operator-list'宏不是強制性的,它的唯一目的是使op-lists更易於閱讀。更具體地說,我想避免列出30多個項目'(op ...(op ...(op ...(op ...') –

相關問題