1.5 規則と式の並びについて
1.5.1 規則の定義と適用
Maximaの規則を利用者が定義する事も可能です.この場合,規則の定義はdefrule函数やlet函
数で行います.
ここでは最初にdefrule函数による規則の定義方法を示しましょう.
defrule函数
¶ ³
defrule(⟨規則名⟩,⟨並び⟩,⟨置換⟩)
µ ´
defrule函数は与えられた⟨並び⟩を指定した⟨置換⟩で置換える規則の定義と規則の名付けを
行う函数です.
この場合,⟨規則名⟩で指定された規則がapply函数族の函数の一つで式に適用されると,⟨並び⟩ に適合する全ての部分式が⟨置換⟩で指定した値で置換されます.尚,照合に失敗すると元の式を返 却します.
次に,defrule函数を使って前置式演算子dfxに適当な規則を入れてみましょう.
(%i1) prefix("dfx");
(%o1) dfx
(%i2) defrule(chain1,dfx(a.b),dfx(a).b+a.dfx(b));
(%o2) chain1 : dfx (a . b) -> dfx a . b + a . dfx b (%i3) apply1(dfx(a.b),chain1);
(%o3) dfx a . b + a . dfx b
(%i4) apply1(dfx(x.y),chain1);
(%o4) dfx (x . y)
最初にdfxが前置式演算子である事をprefix函数で宣言します.次に,dfxがdfx(a.b)→dfx(a).b+a.dfx(b)) となる規則chain1をdefrule函数で定めます.
それからapply1函数を用いてdfx(a.b)に規則chain1を適用すると期待通りの結果を得ます.とこ ろが,dfx(x.y)を計算させると残念な事に,dfx(x.y)のままです. これは何が悪かったのでしょうか?
この例では,式の並びを定義する際に用いた変数に対し,その変数が満すべき条件を定義してい ないからです.Maximaでは変数が満すべき述語が与えられなければ, defrule函数やlet函数で用い られた変数の並びに対してのみ規則が適用される仕様となっています.従って,より一般的な規則 を定義したければ,defrule函数やlet函数で規則を定義する前に,予め⟨並び⟩で用いる変数が満す
matchdeclare函数
¶ ³
matchdeclare(⟨変数⟩,⟨述語函数⟩,· · ·)
matchdeclare([⟨変数1⟩,· · ·,⟨変数1⟩],⟨述語函数⟩,· · ·)
µ ´
matchdeclare函数は引数として⟨変数⟩と⟨述語函数⟩の変数と述語函数名の対を取ります.従っ て,引数は常に偶数個になります.ここで指定する述語函数は,matchdeclareで宣言する変数を与え ると,trueかfalseの何れかが返却される函数です.述語函数として,特に指定する函数が存在しない
場合はtrue,その他には,Maxima組込の述語函数,lambda函数やblock函数を含まない利用者定義
函数が使えます.
例えば, matchdeclare(q,freeof(x,%e))とすれば,変数qはxと%eを含まない全ての式に適合し ます.又,述語には複数の引数を持つ函数で,最後の引数にmatchdeclare函数で宣言する変数が入 るものを与える事も可能です.
尚,matchdeclare函数によって⟨変数⟩にはmatchdeclare属性が付加されます.この属性は
print-props函数で参照する事が可能です.
matchdeclare函数に与える変数と述語函数の対は,後で行う規則の定義で,変数が満すべき条件
を述語函数が与え,この述語がtrueとなる変数に対してのみdefrule等で定めた規則が適用されま す.その為,無条件に規則を適用したければ,述語函数をtrueとします.又,複数の変数が同じ述語 を満す場合 に ⟨変数⟩の個所を⟨変数リスト⟩で置換える事も可能です.
では,matchdeclare函数で並び変数を予め定義し,それからdefrule函数で規則を定義してみま しょう.
(%i1) prefix("dfx");
(%o1) dfx
(%i2) matchdeclare([_a,_b],true);
(%o2) done
(%i3) defrule(chain1,dfx(_a._b),dfx(_a)._b+_a.dfx(_b));
(%o3) chain1 : dfx (_a . _b) -> dfx _a . _b + _a . dfx _b (%i4) apply1(dfx(a.b),chain1);
(%o4) dfx a . b + a . dfx b
(%i5) apply1(dfx(x.y),chain1);
(%o5) dfx x . y + x . dfx y
この例では,変数aと bを常にtrueとし,これらの変数に対して規則をあてはめています.その 為,一般の変数に対しても同様に規則の適用が実行されます.
但し,matchdeclareで宣言した変数の可換積∗ や和+を取る場合には注意が必要です.
(%i7) matchdeclare([_c,_d],true);
(%o7) done
(%i8) defrule(chain1,dfx(_c*_d),dfx(_c)*_d+_c*dfx(_d));
_d _c partitions ‘product’
(%o8) chain1 : dfx (_c _d) -> _c dfx _d + dfx _c _d
この例では,defruleで規則を定義すると,_d _c partitions ‘product’と表示されています.こ
の調子でapply1函数を実行するとまともな処理が出来ずに, Maximaが落ちてしまいます.
matchdeclare函数で宣言した変数の可換積*や羃ˆを含む式に対しては, let函数を用いる事が出
来ますが,上記の例の様に函数の引数に可換積*や羃ˆを含ませる場合には,let函数は有効ではありま せん.寧ろ,非可換積を用いるか,適当な演算子を宣言してdefrule函数を用いる方が良いでしょう.
let函数
¶ ³
let(⟨項⟩,⟨式⟩,⟨述語⟩, ⟨変数1⟩,· · ·,⟨変数n⟩,⟨パッケージ名⟩) let(⟨項⟩,⟨式⟩,⟨述語⟩, ⟨変数⟩,· · ·,⟨変数n⟩)
let(⟨項⟩,⟨式⟩)
µ ´
let函数は⟨述語⟩がtrueの場合,⟨項⟩を⟨式⟩で置換する規則を定義する函数です.
この⟨項⟩には,アトム,sin(x)やf(x,y)の様な函数の可換積∗,商/や羃ˆを含む項となります.但 し,負の羃を用いる場合には大域変数letratをtrueにする必要があります.
⟨式1⟩に含まれる⟨変数i⟩と対応する⟨述語⟩を省く場合には,それらの変数がmatchdeclare函 数によって予めtrueであると宣言されていなければなりません.
let函数の引数の末尾に⟨パッケージ名⟩を追加すれば,定義した置換規則を指定したパッケージ に追加します.未設定の場合,自動的にcurrent let rule packageに割当てられたパッケージに追加 されます.
これらの置換函数は一度に幾つかの規則の組合せを用いて作用させられます. 各々の規則の組合 わせは任意数のlet函数で操作された任意の数の規則を含む事が可能で,利用者が与えた名前で参 照されます.
let函数で定義した規則を式に適用する場合は,letsimp函数を用います.
letsimp函数
¶ ³
letsimp(⟨式⟩,⟨規則パッケージ名1⟩,· · ·,⟨規則パッケージ名n⟩) letsimp(⟨式⟩,⟨規則パッケージ名⟩)
letsimp(⟨式⟩)
µ ´
letsimp函数は⟨式⟩ が指定した規則パッケージに含まれる規則の適用を続けて,式の変化が無
くなるまで規則の適用を続けます.
尚,規則パッケージの指定が無い場合,current let rule packageに割当てられた規則パッケージが 利用されます.
パッケージを複数指定した場合, ⟨式 ⟩ には左端のパッケージから順番に適用されます.例え ば, letsimp(expr,package1,package2)を実行すると,最初にletsimp(expr,package1)を実行し,次 に,letsimp(%,package2)を実行したものと同じ結果が得られます.
ここで規則パッケージを指定してcurrent let rule packageが切替えられる事はありません.
では簡単にlet函数とletsimp函数の動作を確認しておきましょう.
(%i1) matchdeclare([_a,_b],true);
(%o1) done
(%i2) let(tama(_a)^2-1,tama(2*_a));
2
(%o2) tama (_a) - 1 --> tama(2 _a) (%i3) letsimp(tama(x)^2);
2
(%o3) tama (x)
(%i4) let(tama(_a)^2,tama(2*_a)+1);
2
(%o4) tama (_a) --> tama(2 _a) + 1 (%i5) letsimp(tama(x)^2);
(%o5) tama(2 x) + 1
この例では,sin函数の倍角公式を模擬したものです.
最初に, let(tama( aˆ2-1,tama(2* a))としている為,letsimp函数による規則の適用に失敗している 事に注意して下さい.let函数では置換される並びは項に限定されます.
tellsimp一家
¶ ³
tellsimp(⟨並び⟩,⟨置換⟩) tellsimpafter(⟨並び⟩,⟨置換⟩)
µ ´
tellsimp函数とtellsimpafter函数は各々規則に沿った置換を実行します.これらの函数は共に似
ていますが,ここでの置換は,規則を適用する前に置換を行うか,規則を適用した後で置換を行うか の違いがあります.
最初のtellsimp函数は,新しい情報を古い情報の前に置いて,組込の簡易化規則よりも前に適用
します.このtellsimp函数は簡易化が実行される前に式を改変する事が必要な場合に用います.こ
こでの ⟨並び⟩ には和,積, 単変数や数値は使えません.置換には規則名のリストを指定し,defrule 函数, defmatch函数,tellsimp函数やtellsimpafter函数で追加されたものです.
tellsimpafter函数はtellsimp函数と同様に⟨並び⟩に対する⟨置換⟩を定義します.ここで指定
する ⟨並び⟩はMaxima組込みの簡易化規則の適用後に用いるものになります. この⟨並び⟩ に
は単変数や数を除く任意の式が設定出来ます.
defmatch函数
¶ ³
defmatch(⟨函数名⟩,⟨並び⟩,⟨助変数1⟩,· · ·,⟨助変数n⟩)
µ ´
defmatch函数はn+1個の引数を持ち,特定の並びに適合するかどうか式を検査する函数を⟨函
数名⟩で指定した名前で生成する函数です.
defmatch函数の⟨並び⟩ は変数と⟨助変数1⟩,· · ·,⟨助変数n⟩ を含む式になります.ここで変数
が既にmatchdeclare函数で宣言されていたとしても, defmatch函数の ⟨助変数i⟩ の引数として defmatch函数に与えます.
函数の最初の引数は並びに対して照合する式で,他のn個の引数は式中の実際の値, 並びの中で 変数として置かれるものです.
defmatchで構成された函数は照合に成功すると⟨助変数i⟩=適合する変数のリスト を返しま
す. ここで,照合に失敗すればfalseを返します.
次の例では,与えられた函数の線形性を調べる函数linearの定義を行い,函数linearを実行して います.
(%i2) defmatch(linear,a*x+b,x) (%i3) linear(3*z+(y+1)*z+y^2,z);
(%o3) false
(%i4) linear(a*z+b,z);
(%o4) [x = z]
(%i5) nonzeroandfreeof(x,e):=if e#0 and freeof(x,e) then true else false
(%i6) matchdeclare(a,nonzeroandfreeof(x),b,freeof(x)) (%i7) linear(3*z+(y+1)*z+y^2,z);
(%o7) false
(%i8) defmatch(linear,a*x+b,x) (%i9) linear(3*z+(y+1)*z+y^2,z);
2
(%o9) [b = y , a = y + 4, x = z]
最初にdefmatchでlinearを定義し,次に式 3*z+(y+1)*z+y^ 2が変数zの一次式であるかどう か検証しています.この例ではfalseが返却されています. これは最初の変数aとbに関して何等 の情報も無い為,単純にアトムaとアトムbを持たない一次式と判断されてfalseとなっています.
次のa*z+bの場合,並び変数xに対応するものがzであると判断した為に[x = z]が返却されてい
ます.
そこで,並び変数aとbの情報を追加します.この例では,最初にis(e#0 and freeof(x,e))と同値 な函数nonzeroandfreeofを定義します.それから並び変数aとbに対して,共に0ではなく,変数x も含まない式であるとmatchdeclare函数を用いて宣言しています.この宣言の後にdefmatch函数
で函数linearを再定義します. この再定義を行わないと,並び変数aとbに関する情報は更新され
ません.これによって並び変数aとbが変数xから独立したもので,変数xに関して線形であれば, 対応する成分リストを返す函数linearが定義されます.
そこで, linear(3*z+(y+1)*z+yˆ2,z)を実行すると,linearで与式と並びの式a*x+bを比較し,今 度は[b=yˆ2, a=y+4, x=z]を返します.