私はdefpackage
Lisp で足を濡らしており、不名誉なスタートを切りました。つまり、理解できないバグです。
以下のコードは、ベクトルに対して中置操作を実行するためのサブ言語を作成する試みです。線形代数を含むプロジェクトにこれを使用したいと思います。
私のコードの「肉」は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 にあります。