2

私はdefpackageLisp で足を濡らしており、不名誉なスタートを切りました。つまり、理解できないバグです。

以下のコードは、ベクトルに対して中置操作を実行するためのサブ言語を作成する試みです。線形代数を含むプロジェクトにこれを使用したいと思います。

私のコードの「肉」はparse-infix. この関数は、優先度が最も高い演算子を見つけ、apply-opその演算子とそのオペランドを置き換える呼び出しを呼び出してoperator (operand, operand)リストを縮小し、リストに結果のみが含まれるまで繰り返します。サポートされている演算子は、4 つの規則、等価性 (結果を 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

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はマクロであり、呼び出されるまで何もしないラムダ式に展開されます。

しかし、実際にはこのように見えるはずです

次のような演算子の定義が必要です。

(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 に答える