人間は内挿表現に慣れているので、入力するときには必要だが、 Lisp が内部で処理する際には、Lisp 言語に適した前置表現を用いることにする。 内挿表現を前置表現に変換するのはかなり難しい。
| 関数 | 機能 |
|---|---|
| (in2pre expr) | 内挿表現 expr を前置表現に変換する |
in2pre は引数にもらった expr (表現) を他の関数に次々に渡して、 再帰的に変換していく。そのとき optr (オペレータ)、opln (オペランド) はスタックとして使われる。
プログラムリスト: in2pre.el
;;;---------------------------------------------------------------
;;; 内挿表現から前置表現への変換 (by 平野 拓一)
;;;---------------------------------------------------------------
;;;---------------------------------------------------------------
;;; Common Lisp の基本関数を定義する
;;;---------------------------------------------------------------
(defun nequal (x y)
(if (equal x y) nil t)
)
(defun cadr (x)
(car (cdr x))
)
(defun caddr (x)
(car (cdr (cdr x)))
)
(defun cdar (x)
(cdr (car x))
)
(defun caar (x)
(car (car x))
)
(defun cddr (x)
(cdr (cdr x))
)
;;;---------------------------------------------------------------
;;; 演算子の重みを定義する
;;;
;;; 次の例でわかるように、 / は * よりも高い演算順位をもつ
;;; (例) 8/2*4, (8/2)*4=16, 8/(2*4)=1
;;;---------------------------------------------------------------
(defun weight (op)
(cond
((equal op '+) 1)
((equal op '-) 1)
((equal op '*) 2)
((equal op '/) 3)
((equal op '^) 4)
((equal op 'sin) 6)
((equal op 'cos) 6)
((equal op 'exp) 6)
((equal op 'log) 6)
(t 9) ; 数値とか文字はここ
)
)
;;;---------------------------------------------------------------
;;; 内挿表現から前置表現への変換
;;;---------------------------------------------------------------
; 内挿表現から前置表現へ変換する関数
(defun in2pre (expr)
(cond
((atom expr) expr) ; アトムだったらそのまま返す
(t (in2pre_1 expr nil nil))
)
)
; 関数の処理か、それ以外の四則演算・冪乗等の処理を振り分ける
(defun in2pre_1 (expr optr opln)
(cond
;---- condition: +,- 単項演算子のとき
((eq (weight (car expr)) 1)
(in2pre_2 expr optr (cons '0 opln)))
;---- condition: 四則演算・冪乗等のとき
; expr は expr の後ろの部分
; optr はそのまま
; opln に expr の前の部分を追加
((or (< (weight (car expr)) 5)
(> (weight (car expr)) 7))
(in2pre_2 (cdr expr) optr (cons (in2pre (car expr)) opln)))
;---- condition: 関数のとき
(t (in2pre_3 (cddr expr)
optr
(cons (list (car expr) (in2pre (cadr expr))) opln)))
)
)
; 四則演算・冪乗等の処理
(defun in2pre_2 (expr optr opln)
(cond
;---- condition: ((expr が空) and (optr が空))
;
; opln の先頭を返す
((and (equal expr nil) (equal optr nil))
(car opln))
;---- condition: (expr が空でない) and ((optr が空) or
; ((expr 先頭の演算子の重み) > (optr 先頭の演算子の重み)))
;
; expr は expr の後ろの部分
; optr に expr 先頭のオペレータを追加
; opln はそのまま
((and (nequal expr nil)
(or (equal optr nil) (> (weight (car expr))
(weight (car optr)))))
(in2pre_1 (cdr expr) (cons (car expr) optr) opln))
;---- condition: それ以外のとき
; つまり、前の optr に格納されている演算子よりも
; 今から処理する expr 先頭の演算子のが重みが重いとき
;
; expr はそのまま
; optr はスタック先頭を除いた後ろの部分
; opln は optr から取り出した 演算子を使って前置表現にする
(t (in2pre_2 expr
(cdr optr)
(cons (list (car optr)
(cadr opln)
(car opln))
(cddr opln))))
)
)
; 関数の処理
(defun in2pre_3 (expr optr opln)
(cond
;---- condition: ((expr が空) and (optr が空))
;
; opln の先頭を返す
((and (equal expr nil) (equal optr nil))
(car opln))
;---- condition: ((expr が空でない) and
; ((optr が空) or (expr 先頭の演算子の重みがその次のより重い)))
;
; expr は先頭を取り除く
; optr は expr の先頭のものを追加する
; opln はそのまま
((and (nequal expr nil)
(or (equal optr nil) (> (weight (car expr))
(weight (cadr expr)))))
(in2pre_1 (cdr expr) (cons (car expr) optr) opln))
;---- condition: その他のとき
; expr の先頭の演算子の重みはその次の重みよりも軽いので、
;
; expr はそのまま
; optr はそのまま
; opln は opln の前の2つのリスト
(t (in2pre_2 expr optr opln))
)
)
;;;---------------------------------------------------------------
;;; 確認テスト
;;;---------------------------------------------------------------
; 変数 expr に式を代入する
(setq expr '((1 + 3 * x) * 2 + sin (- x ^ 2) * log ( a )))
; 内挿表現を前置表現に変換する
(in2pre expr)
(in2pre '(8 / 2 * 4))
;;
;; End of file
;;
| 入力 | (setq expr '((1 + 3 * x) * 2 + sin (- x ^ 2) * log ( a ))) (in2pre expr) |
|---|---|
| 出力 | (+ (* (+1 (*3 x)) 2) (* (sin (- 0 (^ x 2))) (log a))) |