HOME | Common Lisp | code | 書き込む |
(defun igas-p (v n d) (/ (* 8.31415 n d) v)) (defun igas-v (p n d) (/ (* 8.31415 n d) p)) (defun igas-n (p v d) (/ (* p v) 8.31415 d)) (defun igas-t (p v n) (/ (* p v) 8.31415 n))これはいささか不便で、4つの変数の値が決まれば残りの変数の値を計算してくれる 双方向あるいは無方向の”計算素子”があると便利です。
いわゆる双方向計算は双方向計算素子のネットワーク上で、”制約”を伝播させることによって 実現されますが、マクロを使うとほぼ同等なことが容易に実現できます。 マクロは式を生成し、その式を評価して値を得るので、双方向計算にはうってつけの手法です。 次の様なマクロを書くことによって別個の関数を作ることなしに任意の変数の値を求めることが出来ます。
; check if given list has single element (defun single (obj) (and (car obj) (not (cdr obj)))) ; group items in the list such as (a 1 b 2 c 3) --> ((a 1) (b 2) (c 3)) (defun group (source n) (if (zerop n) (error "zero length")) (labels ((rec (source acc) (let ((rest (nthcdr n source))) (if (consp rest) (rec rest (cons (subseq source 0 n) acc)) (nreverse (cons source acc)))))) (if source (rec source nil) nil))) ;a macro of bidirectional calculations for ideal gases (defmacro igas0 (&rest av) (let* ((parm (group av 2)) (unknown (set-difference '(p v n d) (mapcar #'car parm))) (r 8.31415)) (unless (single unknown) (error "Invalid unknown.")) `(let ((*WARN-ON-FLOATING-POINT-CONTAGION* nil)) (let ,parm ,(case (car unknown) (p `(/ (* ,r n d) v)) (v `(/ (* ,r n d) p)) (n `(/ (* p v) ,r d)) (d `(/ (* p v) ,r n))))))) ;;; 展開形 >(pprint (macroexpand-1 '(igas0 p 1.0e5 n 1.0 d 300))) (let ((*WARN-ON-FLOATING-POINT-CONTAGION* nil)) (let ((p 100000.0) (n 1.0) (d 300)) (/ (* 8.31415 n d) p))) ;;; 実行例 >(igas0 p 1.0e5 n 1.0 d 300) 0.02494245(igas0 p 1.0e5 n 1.0 d 300)
; 理想気体の状態方程式, PV = nRT (defformula igas (- (* p v) (* n 8.31451 d))) ; van der Waals の実在気体の状態方程式, (P + an2/V2)(V - nb) = nRT** (defformula rgas (- (* (+ p (/ (* a (expt n 2)) (expt v 2))) (- v (* n b))) (* n 8.31451 d)))
例:(赤い文字は入力)
>(igas p 1.0e5 v 0.024 d 300) 0.96217334 ; N >(igas -h) (/ (* P V) N 8.31451 D) >(rgas p 1.0d5 n 1.0 d 300 a 1.35d-7 b 3.66d-5) give r-min, r-max, and epsilon for V ; 解の範囲と誤差を聞いてくる。 0.02 0.03 1.0e-6 ; 3つの値をスペースで区切って与える。 0.0249802 ; ; 変数の値 V ; 変数の名前
行 | 説明 |
---|---|
11. | function (single obj): obj が要素1つのリストかどうか調べます。 |
14. | function (group source n): source を n 個ずつまとめたリストを返します。 (group '(a 1 b 2 c 3) 2) --> ((a 1) (b 2) (c 3)) |
23. | function (find-tree obj tree): obj が tree (ネストしたリスト)に含まれるか調べます。 |
31. | function (position-tree target lst): tree のリスト lst で target が含まれる tree の位置を返します。 |
36. | function (remove-nth n lst): lst の n 番目の要素を除いたリストを返します。 |
37. | function (get-par form): form に含まれる変数のリストを返します。 |
61. | function (solve target form): form を解析的に解いて target を求める式を返します。 この関数は、逆関数のハッシュテーブルを定義したクロージャーに囲まれています。 |
62. | local function (rec this that): solve 内部の局所関数。 (eq this target) になるように再帰的に式を変換します。コードは一見複雑ですが、 式を解くとき人間が紙に書いてやっていることを LISP で表しただけです。 |
99. | function (devide-a-n ls): リスト ls の要素のうち、ls 一度しか出現しないもののリストを第一要素に、
複数回出現するもののリストを第二要素にするリストを返します。 (devide-a-n '(a b a b a b c d e)) --> ((e d c) (b a)) |
111. | function (bisec f min max epsilon) 逐次2分法で f の解を min -- max の範囲で誤差 epsilon で求めます。 |
125. | functin (round-float fl c): 浮動小数点数 fl を c で四捨五入します。表示の整形に使います。 |
131. | function (nansw f v): 数値的に解を求める関数です。 |
141. | macro (ave &rest av): av の平均を求めます。関数定義より早くなります。 |
144. | macro (defformula name form &rest forget):双方向計算マクロを定義するマクロです。 展開形が igas0の様になるようになっています。 |
156. | *WARN-ON-FLOATING-POINT-CONTAGION* が non nil だと違う精度の浮動小数点間で演算を行ったとき警告を発します。 このマクロではそのような警告はわずらわしいのでこの値を nil に設定。 |
160. | マクロ igas0 の この部分を自動的に生成する式です。たった一行でかなり省力化します。 |
162. | 解析的に求められない変数を逐次二分法で求める部分です。 |
171. | macro (igas &rest av): igas0 を defformula を使って定義しなおしたもの。 |
173. | macro (rgas &rest av): van der Waals の状態方程式を defformula を使って定義したもの。 |
180. | macro (air &rest av): 空気の状態方程式。 |
183. | macro (co2 &rest av): 二酸化炭素の状態方程式。 |
188. | macro (fc &rest av): 華氏、セ氏変換。 |
双方向計算は、マクロを使うと容易に実現でき、しかも、関数を使って実装するのは困難な課題です。 (相当控えめに言ってもこのような形で変数を受け取る関数は マクロを書くよりも長くなります。) このページでは一歩進めて、双方向計算マクロを生成するマクロを書いてみました。 ここでは高校や大学でならう気体の状態方程式を例にしていますが、他の分野のほとんどの公式で使えます。
皆様のご参考になれば幸いです。
HOME | Common Lisp | code | 書き込む |