Next: , Previous:   [Contents][Index]

34 Rules and Patterns


34.1 Introduction to Rules and Patterns

この節ではユーザー定義のパターンマッチングと整理ルールを記述します。 幾分違ったパターンマッチング体系を実装した2つの関数グループがあります。 1つのグループは、tellsimp, tellsimpafter, defmatch, defrule, apply1, applyb1, apply2です。 他のグループは、let, letsimpです。 どちらの体系も、 matchdeclareが宣言したパターン変数を使ってパターンを定義します。

tellsimptellsimpafterが定義するパターンマッチングルールは、 Maximaの整理器によって自動的に適用されます。 defmatch, defrule, letによって定義されたルールは、 明示的に関数をコールすることで適用されます。

更に、tellratが多項式に適用するルール用メカニズムと、 affineパッケージの可換/非可換代数用メカニズムがあります。


34.2 Functions and Variables for Rules and Patterns

関数: apply1 (expr, rule_1, ..., rule_n)

rule_1exprに、失敗するまで繰り返し適用して、 それから同じルールを exprの部分式すべてに左から右へ、 部分式すべてで rule_1が失敗するまで繰り返し適用します。

この方法で exprを変換した結果を expr_2と呼ぶことにします。 次に、 rule_2expr_2の最上部から始めて同じ方法で適用します。 rule_nが最後の部分式上で失敗する時、結果が返されます。

maxapplydepthは、 apply1apply2が処理する最も深い部分式の深さです。

applyb1, apply2, letも参照してください。

関数: apply2 (expr, rule_1, ..., rule_n)

もしrule_1が与えられた部分式上で失敗したら、 rule_2が繰り返し適用されます、などなど。 すべてのルールが与えられた部分式上で失敗した時だけ、 ルールの全組が次の部分式に繰り返し適用されます。 もしルールの1つが成功したら、 同じ部分式が最初のルールから再処理されます。

maxapplydepthは、 apply1apply2が処理する最も深い部分式の深さです。

apply1letも参照してください。

関数: applyb1 (expr, rule_1, ..., rule_n)

失敗するまで、繰り返し exprの最も深い部分式に rule_1を適用し、 その後、 rule_1がトップレベルの式で失敗するまで、 同じルールを1つ高いレベル(すなわち、より大きな部分式)に適用します。 その後、rule_2rule_1の結果に 同様に適用されます。 rule_nがトップレベルの式に適用された後、結果が返されます。

applyb1apply1に似ていますが、 トップダウンからの代わりにボトムアップから働きます。

maxapplyheightapplyb1が、あきらめる前に届く 最大の高さです

apply1, apply2, letも参照してください。

オプション変数: current_let_rule_package

デフォルト値: default_let_rule_package

current_let_rule_packageは、 もし他のルールパッケージが指定されないなら、 (letsimpなど)letパッケージの関数で使われる ルールパッケージの名前です。 この変数は、letコマンドを介して定義された任意の規格パッケージの名前を 割り当てられます。

もし letsimp (expr, rule_pkg_name)のようなコールがされたら、 ルールパッケージrule_pkg_nameは その関数コールだけのために使われ、 current_let_rule_packageの値は変わりません。

オプション変数: default_let_rule_package

デフォルト値: default_let_rule_package

default_let_rule_packageは、 letでユーザーによって、 また、current_let_rule_packageの値を変更することによって、 陽に設定されない時使われる ルールルールの名前です。

関数: defmatch (progname, pattern, x_1, …, x_n)
関数: defmatch (progname, pattern)

patternにマッチするか見るために exprをテストする 関数progname(expr, x_1, ..., x_n) を定義します。

patternは、 (引数として与えられているなら) パターン引数 x_1, ..., x_nを含む式です。 パターン引数は defmatchの引数として明示的に与えます。 一方、matchdeclare関数がパターン変数を宣言します。 matchdeclareのパターン変数か defmatchのパターン引数として宣言されていない 任意の変数はそれ自身とのみマッチします。

生成関数prognameの最初の引数はパターンに対してマッチされる式であり、 他の引数は、パターンの中のダミー変数x_1, ..., x_nに対応する 実際の引数です。

もしマッチが成功したなら、 prognameは、 左辺がパターン引数やパターン変数で、右辺がパターン引数や変数がマッチした部分式の 等式のリストを返します。 パターン変数はそれらがマッチした部分式に割り当てられますが、 パターン引数には割り当てられません。 もしマッチが失敗したら、 prognamefalseを返します。

リテラルパターン(すなわち、パターン引数もパターン変数も含まないパターン)は、 もしマッチが成功したら、 trueを返します。

matchdeclare, defrule, tellsimp, tellsimpafterも 参照してください。

例:

abxを含まず、 aが非ゼロであるような 形式a*x + bかどうか見るために exprをテストする 関数linearp(expr, x)を定義します。 パターン引数xdefmatchに与えられているので、 このマッチ関数は、 任意の変数に関する線形式にマッチします。

(%i1) matchdeclare (a, lambda ([e], e#0 and freeof(x, e)), b,
                    freeof(x));
(%o1)                         done
(%i2) defmatch (linearp, a*x + b, x);
(%o2)                        linearp
(%i3) linearp (3*z + (y + 1)*z + y^2, z);
                         2
(%o3)              [b = y , a = y + 4, x = z]
(%i4) a;
(%o4)                         y + 4
(%i5) b;
                                2
(%o5)                          y
(%i6) x;
(%o6)                           x

abxを含まず、 aが非ゼロであるような 形式a*x + bかどうか見るために exprをテストする 関数linearp(expr, x)を定義します。 defmatchにパターン引数が与えられていないので、 このマッチ関数は、 他の任意の変数ではなく変数xに関する線形式にマッチします。

(%i1) matchdeclare (a, lambda ([e], e#0 and freeof(x, e)), b,
                    freeof(x));
(%o1)                         done
(%i2) defmatch (linearp, a*x + b);
(%o2)                        linearp
(%i3) linearp (3*z + (y + 1)*z + y^2);
(%o3)                         false
(%i4) linearp (3*x + (y + 1)*x + y^2);
                             2
(%o4)                  [b = y , a = y + 4]

定積分かどうか見るために exprをテストする 関数checklimits(expr)を定義します。

(%i1) matchdeclare ([a, f], true);
(%o1)                         done
(%i2) constinterval (l, h) := constantp (h - l);
(%o2)        constinterval(l, h) := constantp(h - l)
(%i3) matchdeclare (b, constinterval (a));
(%o3)                         done
(%i4) matchdeclare (x, atom);
(%o4)                         done
(%i5) simp : false;
(%o5)                         false
(%i6) defmatch (checklimits, 'integrate (f, x, a, b));
(%o6)                      checklimits
(%i7) simp : true;
(%o7)                         true
(%i8) 'integrate (sin(t), t, %pi + x, 2*%pi + x);
                       x + 2 %pi
                      /
                      [
(%o8)                 I          sin(t) dt
                      ]
                      /
                       x + %pi
(%i9) checklimits (%);
(%o9)    [b = x + 2 %pi, a = x + %pi, x = t, f = sin(t)]
関数: defrule (rulename, pattern, replacement)

与えられたパターンに関する置き換えルールを定義し、名付けます。 もしrulenameと名付けられたルールが (apply1, applyb1, apply2によって) 式に適用されるなら、 パターンにマッチするすべての部分式はreplacementで置き換えられます。 パターンマッチが値を割り当てるreplacementの中の変数すべては その後整理される置き換えの中のそれらの値を割り当てられます。

ルールそれ自身は、 パターンマッチと置き換えの1演算で式を変換する 関数として扱うことができます。 マッチが失敗したら、ルール関数はfalseを返します。

関数: disprule (rulename_1, ..., rulename_2)
関数: disprule (all)

defrule, tellsimp, tellsimpafterが返すような、または defmatchが定義するパターンのような、 名前rulename_1, ..., rulename_nを持つルールを 表示します。 ルールそれぞれは中間式ラベル(%t)と一緒に表示されます。

disprule (all)は、ルールすべてを表示します。

dispruleは引数をクォートします。 dispruleは、 表示されたルールに対応する中間式ラベルのリストを返します。

letが定義したルールを表示する letrulesも参照してください。

例:

(%i1) tellsimpafter (foo (x, y), bar (x) + baz (y));
(%o1)                   [foorule1, false]
(%i2) tellsimpafter (x + y, special_add (x, y));
(%o2)                   [+rule1, simplus]
(%i3) defmatch (quux, mumble (x));
(%o3)                         quux
(%i4) disprule (foorule1, "+rule1", quux);
(%t4)        foorule1 : foo(x, y) -> baz(y) + bar(x)

(%t5)          +rule1 : y + x -> special_add(x, y)

(%t6)                quux : mumble(x) -> []

(%o6)                    [%t4, %t5, %t6]
(%i6) ''%;
(%o6) [foorule1 : foo(x, y) -> baz(y) + bar(x), 
     +rule1 : y + x -> special_add(x, y), quux : mumble(x) -> []]
関数: let (prod, repl, predname, arg_1, ..., arg_n)
関数: let ([prod, repl, predname, arg_1, ..., arg_n], package_name)

prodreplで置き換えられるような letsimpのための代入ルールを定義します。 prodは、以下の項の正または負のべきの積です:

  • letsimpをコールする以前にmatchdeclare関数が 述語論理をアトムと関連づけるために使われないなら、 letsimpが文字通りに検索するアトム。 この場合、letsimpはアトムを述語論理を満たす積の任意の項にマッチさせます。
  • sin(x), n!, f(x,y)などのようなカーネル。 上のアトムと同様に、 述語論理をカーネルの引数に関連づけるために matchdeclareが使われないなら letsimpは文字通りのマッチを検索します。

正のべきの項は、少なくともそのべきを持つ項だけにマッチするでしょう。 一方、負のべきの項は、少なくとも負としてのべきをもつ項だけにマッチするでしょう。 prodの中の負のべきの場合、 スイッチletrattrueに設定しなければいけません。 letratも参照してください。

もし述語論理が 引数のリストが続くlet関数に含まれるなら、 arg_i’arg_iにマッチした値である場合、 predname (arg_1', ..., arg_n')trueに評価される時だけ 試験的なマッチ(すなわち、述語論理が省略されたなら受け入れられるもの) が受け入れられます。 arg_iは、任意のアトム名や prodの中に現れる任意の核の引数を取り得ます。 replは、任意の有理式を取り得ます。 もし 任意のアトムやprodからの引数がreplの中に現れるなら、 適切な代入が行われます。

グローバルフラグletratは、 letsimpによる商の整理を制御します。 letratfalseの時、 letsimpは、 exprの分子と分母をそれぞれ整理し、章を整理はしません。 n!/nのような代入は(n-1)!に進み失敗します。 letrattrueの時、 分子、分母、商がその順番で整理されます。

これらの代入関数は、同時にいくつかのルールパッケージを使うことを許します。 ルールパッケージそれぞれは、 任意の数のletルールを含むことができ、ユーザー定義名で参照されます。 コマンドlet ([prod, repl, predname, arg_1, ..., arg_n], package_name)は、 ルールprednameをルールパッケージpackage_nameに加えます。 コマンドletsimp (expr, package_name) は、 package_nameの中でルールを適用します。 letsimp (expr, package_name1, package_name2, ...)は、 letsimp (%, package_name2), …が続く letsimp (expr, package_name1)と同値です。

current_let_rule_packageは、 現在使われているルールパッケージの名前です。 この変数は、 letコマンドを介して定義された任意のルールパッケージの名前に割れ当てられます。 letパッケージを構成する関数のいずれかがパッケージ名なしでコールされた時はいつでも current_let_rule_packageが指定したパッケージが使われます。 もし letsimp (expr, rule_pkg_name)のようなコールがされたら、 ルールパッケージrule_pkg_nameは、 そのletsimpコマンドだけで使われ、 current_let_rule_packageは変わりません。 もし他に指定されないなら、 current_let_rule_packagedefault_let_rule_packageをデフォルト値とします。

(%i1) matchdeclare ([a, a1, a2], true)$
(%i2) oneless (x, y) := is (x = y-1)$
(%i3) let (a1*a2!, a1!, oneless, a2, a1);
(%o3)         a1 a2! --> a1! where oneless(a2, a1)
(%i4) letrat: true$
(%i5) let (a1!/a1, (a1-1)!);
                        a1!
(%o5)                   --- --> (a1 - 1)!
                        a1
(%i6) letsimp (n*m!*(n-1)!/m);
(%o6)                      (m - 1)! n!
(%i7) let (sin(a)^2, 1 - cos(a)^2);
                        2               2
(%o7)                sin (a) --> 1 - cos (a)
(%i8) letsimp (sin(x)^4);
                        4           2
(%o8)                cos (x) - 2 cos (x) + 1
オプション変数: letrat

デフォルト値: false

letratfalseの時、 letsimpは 比の分子と分母それぞれを整理し、 商を整理しません。

letrattrueの時、 分子、分母、商はその順番に整理されます。

(%i1) matchdeclare (n, true)$
(%i2) let (n!/n, (n-1)!);
                         n!
(%o2)                    -- --> (n - 1)!
                         n
(%i3) letrat: false$
(%i4) letsimp (a!/a);
                               a!
(%o4)                          --
                               a
(%i5) letrat: true$
(%i6) letsimp (a!/a);
(%o6)                       (a - 1)!
関数: letrules ()
関数: letrules (package_name)

ルールパッケージのルールを表示します。 letrules ()は現在のルールパッケージのルールを表示します。 letrules (package_name)package_nameのルールを表示します。

現在のルールパッケージは current_let_rule_packageによって指名されます。 もし他に指定されないなら、 current_let_rule_packagedefault_let_rule_packageがデフォルト値になります。

dispruleも参照してください それは、 tellsimptellsimpafterが定義するルールを表示します。

関数: letsimp (expr)
関数: letsimp (expr, package_name)
関数: letsimp (expr, package_name_1, …, package_name_n)

exprに変化がなくなるまで 繰り返し、letが定義する代入ルールを適用します。

letsimp (expr)current_let_rule_packageからルールを使います。

letsimp (expr, package_name)は、 current_let_rule_packageを変えることなしに package_nameからルールを使います。

letsimp (expr, package_name_1, ..., package_name_n)は、 quivalent to letsimp (%, package_name_2)などが続く letsimp (expr, package_name_1と同値です。

オプション変数: let_rule_packages

デフォルト値: [default_let_rule_package]

let_rule_packagesは、 デフォルトパッケージdefault_let_rule_packageに加える ユーザー定義のletルールパッケージすべてのリストです。

関数: matchdeclare (a_1, pred_1, ..., a_n, pred_n)

述語論理 pred_kを変数 a_kの変数やリストに関連づけます。 なので、 a_kは述語論理が false以外の何かを返す式にマッチします。

述語論理は関数の名前、ラムダ式、関数コール、最後の引数のないラムダコール、 trueまたは allです。 任意の式が trueallにマッチします。 もし述語論理が関数コールかラムダコールとして指定されるなら、 テストされる式が引数のリストに追加されます; マッチが評価される時引数が評価されます。 そうでないなら、述語論理は関数名またはラムダ式として指定され、 テストされる式が唯一の引数です。 matchdeclareがコールされた時、述語論理関数は定義されている必要はありません; 述語論理はマッチが試みられるまで評価されません。

述語論理は、 truefalseはもちろん、ブーリアン式を返すかもしれません。 ブーリアン式は、構成されたルール関数内で isによって評価されるので、 述語論理内部で isをコールする必要はありません。

もし式がマッチ述語論理を満たすなら、 マッチ変数が式に割り当てられます。 例外は足し算 +や掛け算 *のオペランドのマッチ変数です。 足し算と掛け算だけは特別に扱われます; 他のn項演算子(組み込みもユーザー定義も)は通常の関数のように扱われます。

足し算と掛け算の場合、 マッチ変数はマッチ述語論理を満たす唯一の式か、 そんな式の(それぞれ)和または積に割り当てられます。 そんな多項マッチングはどん欲(greedy)です: 述語論理群はそれらの関連変数がマッチパターンの中で現れる順に評価され、 複数の述語論理を満たす項は、それが満たす最初の述語論理によってマッチされます。 述語論理それぞれは、 次の述語論理が評価される前に 和や積のオペランドすべてに対してテストされます。 加えて、 もし(それぞれ)0か1がマッチ述語論理を満たし、かつ、 述語論理を満たす他の項がないなら、 0か1が述語論理の関連マッチ変数に割り当てられます。

足し算と掛け算パターンを処理するアルゴリズムは、 (例えば、「任意のものにマッチする」変数が現れるパターンのように) マッチパターンの中やマッチされる式の中の項の順序付けに依存したいくつかのマッチ結果をもたらします。 しかしながら、もしマッチ述語論理すべてが相互に排他的なら、 1つのマッチ述語論理はべつのものがマッチした項を受け入れられないので、 マッチ結果は順序付けに影響されません。

変数 aを引数として matchdeclareをコールすると、 もし既に宣言されているなら、 aに関する matchdeclareプロパティが変わります: ルールが定義された時、直近の matchdeclareだけが効果を持ちます。 (matchdeclareremoveを介した) matchdeclareプロパティへの後の変更は、存在するルールに影響しません。

propvars (matchdeclare)matchdeclareプロパティを持つ変数すべてのリストを返します。 printprops (a, matchdeclare)は、 変数 aに関する述語論理を返します。 printprops (all, matchdeclare)は、 すべての matchdeclare変数に関する述語論理のリストを返します。 remove (a, matchdeclare)は、 aから matchdeclareプロパティを削除します。

関数 defmatch, defrule, tellsimp, tellsimpafter, letは、パターンに対して式をテストするルールを構成します。

matchdeclareは引数をクォートします。 matchdeclareはいつも doneを返します。

例:

述語論理は、関数名か、ラムダ式か、最後の引数がない関数コールかラムダコールか、 trueallです。

(%i1) matchdeclare (aa, integerp);
(%o1)                         done
(%i2) matchdeclare (bb, lambda ([x], x > 0));
(%o2)                         done
(%i3) matchdeclare (cc, freeof (%e, %pi, %i));
(%o3)                         done
(%i4) matchdeclare (dd, lambda ([x, y], gcd (x, y) = 1) (1728));
(%o4)                         done
(%i5) matchdeclare (ee, true);
(%o5)                         done
(%i6) matchdeclare (ff, all);
(%o6)                         done

もし式がマッチ述語論理を満たすなら、 マッチ変数は式に割り当てられます。

(%i1) matchdeclare (aa, integerp, bb, atom);
(%o1)                         done
(%i2) defrule (r1, bb^aa, ["integer" = aa, "atom" = bb]);
                    aa
(%o2)        r1 : bb   -> [integer = aa, atom = bb]
(%i3) r1 (%pi^8);
(%o3)               [integer = 8, atom = %pi]

足し算と掛け算の場合、 マッチ変数は、マッチ述語論理を満たす1つの式か、 そんな式の(それぞれ)和か積に割り当てられるかもしれません。

(%i1) matchdeclare (aa, atom, bb, lambda ([x], not atom(x)));
(%o1)                         done
(%i2) defrule (r1, aa + bb, ["all atoms" = aa, "all nonatoms" =
               bb]);
bb + aa partitions `sum'
(%o2)  r1 : bb + aa -> [all atoms = aa, all nonatoms = bb]
(%i3) r1 (8 + a*b + sin(x));
(%o3)     [all atoms = 8, all nonatoms = sin(x) + a b]
(%i4) defrule (r2, aa * bb, ["all atoms" = aa, "all nonatoms" =
               bb]);
bb aa partitions `product'
(%o4)   r2 : aa bb -> [all atoms = aa, all nonatoms = bb]
(%i5) r2 (8 * (a + b) * sin(x));
(%o5)    [all atoms = 8, all nonatoms = (b + a) sin(x)]

+*の引数をマッチする時、 もしマッチ述語論理すべてが相互に排他的なら、 1つのマッチ述語論理は別のものがマッチした項を受け入れられないので、 マッチ結果は順序付けに影響されません。

(%i1) matchdeclare (aa, atom, bb, lambda ([x], not atom(x)));
(%o1)                         done
(%i2) defrule (r1, aa + bb, ["all atoms" = aa, "all nonatoms" =
               bb]);
bb + aa partitions `sum'
(%o2)  r1 : bb + aa -> [all atoms = aa, all nonatoms = bb]
(%i3) r1 (8 + a*b + %pi + sin(x) - c + 2^n);
                                                     n
(%o3) [all atoms = %pi + 8, all nonatoms = sin(x) + 2  - c + a b]
(%i4) defrule (r2, aa * bb, ["all atoms" = aa, "all nonatoms" =
               bb]);
bb aa partitions `product'
(%o4)   r2 : aa bb -> [all atoms = aa, all nonatoms = bb]
(%i5) r2 (8 * (a + b) * %pi * sin(x) / c * 2^n);
                                                  n
                                         (b + a) 2  sin(x)
(%o5) [all atoms = 8 %pi, all nonatoms = -----------------]
                                                 c

関数 propvarsprintpropsはマッチ変数についての情報を返します。

(%i1) matchdeclare ([aa, bb, cc], atom, [dd, ee], integerp);
(%o1)                         done
(%i2) matchdeclare (ff, floatnump, gg, lambda ([x], x > 100));
(%o2)                         done
(%i3) propvars (matchdeclare);
(%o3)             [aa, bb, cc, dd, ee, ff, gg]
(%i4) printprops (ee, matchdeclare);
(%o4)                    [integerp(ee)]
(%i5) printprops (gg, matchdeclare);
(%o5)              [lambda([x], x > 100, gg)]
(%i6) printprops (all, matchdeclare);
(%o6) [lambda([x], x > 100, gg), floatnump(ff), integerp(ee), 
                      integerp(dd), atom(cc), atom(bb), atom(aa)]
オプション変数: maxapplydepth

デフォルト値: 10000

maxapplydepthapply1apply2が探索する 最大深さです。

オプション変数: maxapplyheight

デフォルト値: 10000

maxapplyheightapplyb1があきらめる前に到達する 最大高さです。

関数: remlet (prod, name)
関数: remlet ()
関数: remlet (all)
関数: remlet (all, name)

let関数で直近に定義された 代入ルールprod –> replを削除します。 もし名前が供給されるなら、ルールはルールパッケージ名から削除されます。

remlet()remlet(all)は 現在のルールパッケージから代入ルールすべてを削除します。 もし例えば、remlet (all, name)にように、ルールパッケージ名が供給されるなら、 ルールパッケージnameも削除されます。

もし代入が同じ積を使って変更されるなら、 remletはコールされる必要はなく、 let関数と新しい置き換え かつ/または述語論理名で、文字通りに同じ積を使って代入を再定義だけです。 さて、remlet (prod)がコールされると、 元の代入ルールが生き返ります。

remruleも参照してください。 tellsimptellsimpafterで定義されたルールを削除します。

関数: remrule (op, rulename)
関数: remrule (op, all)

tellsimptellsimpafterで定義されたルールを削除します。

remrule (op, rulename)は、 演算子opから 名前rulenameを持つ ルールを削除します。 opが組み込みか (infix, prefix, などで定義されたような)ユーザー定義演算子の時、 oprulenameはダブルクォートマークでくくられないといけません。

remrule (op, all)は 演算子opに関するルールすべてを削除します。

remletも参照してください。 letで定義されたルールを削除します。

例:

(%i1) tellsimp (foo (aa, bb), bb - aa);
(%o1)                   [foorule1, false]
(%i2) tellsimpafter (aa + bb, special_add (aa, bb));
(%o2)                   [+rule1, simplus]
(%i3) infix ("@@");
(%o3)                          @@
(%i4) tellsimp (aa @@ bb, bb/aa);
(%o4)                   [@@rule1, false]
(%i5) tellsimpafter (quux (%pi, %e), %pi - %e);
(%o5)                  [quuxrule1, false]
(%i6) tellsimpafter (quux (%e, %pi), %pi + %e);
(%o6)             [quuxrule2, quuxrule1, false]
(%i7) [foo (aa, bb), aa + bb, aa @@ bb, quux (%pi, %e),
       quux (%e, %pi)];
                                     bb
(%o7) [bb - aa, special_add(aa, bb), --, %pi - %e, %pi + %e]
                                     aa
(%i8) remrule (foo, foorule1);
(%o8)                          foo
(%i9) remrule ("+", ?\+rule1);
(%o9)                           +
(%i10) remrule ("@@", ?\@\@rule1);
(%o10)                         @@
(%i11) remrule (quux, all);
(%o11)                        quux
(%i12) [foo (aa, bb), aa + bb, aa @@ bb, quux (%pi, %e),
        quux (%e, %pi)];
(%o12) [foo(aa, bb), bb + aa, aa @@ bb, quux(%pi, %e), 
                                         quux(%e, %pi)]
関数: tellsimp (pattern, replacement)

tellsimpafterに似ていますが、 古いものの前に新しい情報を置くので、 組み込み整理ルールの前に適用されます。

整理器が働く前に式を変更することが重要な時 例えば、整理器が式について何か「知っている」が、それが返すものが好みでないなら、 tellsimpが使われます。 もし整理器が式の主演算子について何かを「知っている」が、単に十分でないなら、 たぶんtellsimpafterを使いたいでしょう。

パターンは和、積、変数1つ、または、数は取れません。

システム変数rulesは、 defrule, defmatch, tellsimp, tellsimpafterで 定義されたルールのリストです。

例:

(%i1) matchdeclare (x, freeof (%i));
(%o1)                         done
(%i2) %iargs: false$
(%i3) tellsimp (sin(%i*x), %i*sinh(x));
(%o3)                 [sinrule1, simp-%sin]
(%i4) trigexpand (sin (%i*y + x));
(%o4)         sin(x) cos(%i y) + %i cos(x) sinh(y)
(%i5) %iargs:true$
(%i6) errcatch(0^0);
 0
0  has been generated
(%o6)                          []
(%i7) ev (tellsimp (0^0, 1), simp: false);
(%o7)                  [^rule1, simpexpt]
(%i8) 0^0;
(%o8)                           1
(%i9) remrule ("^", %th(2)[1]);
(%o9)                           ^
(%i10) tellsimp (sin(x)^2, 1 - cos(x)^2);
(%o10)                 [^rule2, simpexpt]
(%i11) (1 + sin(x))^2;
                                      2
(%o11)                    (sin(x) + 1)
(%i12) expand (%);
                                   2
(%o12)               2 sin(x) - cos (x) + 2
(%i13) sin(x)^2;
                                  2
(%o13)                     1 - cos (x)
(%i14) kill (rules);
(%o14)                        done
(%i15) matchdeclare (a, true);
(%o15)                        done
(%i16) tellsimp (sin(a)^2, 1 - cos(a)^2);
(%o16)                 [^rule3, simpexpt]
(%i17) sin(y)^2;
                                  2
(%o17)                     1 - cos (y)
関数: tellsimpafter (pattern, replacement)

組み込み整理ルールの後、適Maxima整理器が適用する 整理ルールを定義します。 patternは (matchdeclareで宣言された) パターン変数や他のアトムや演算子から成る、 パターンマッチングの目的でリテラルと考えられる式です。 replacementは、 patternにマッチする実際の式に代入されます; replacementの中のパターン変数は 実際の式の中でマッチした値に割り当てられます。

patternは、 主演算子がパターン変数でない任意の非アトム式を取り得ます; 整理ルールは主演算子に関連付けられます。 (以下で記述する1つの例外がありますが、)関数、リスト、配列の名前が、 (パターン変数でなく)ただリテラルとして、 主演算子としてpatternの中で現れることができます; これは、 パターンとして もしaabbがパターン変数なら aa(x)bb[y]のような式を除外します。 パターン変数である関数、リスト、配列の名前は、 patternの中で、主演算子以外の演算子として現れることができます。

上の関数名に関するルールに1つ例外があります。 aa[x](y)のような式の中の添字付き関数の名前は 主演算子がaaでなくLispアトムmqapplyだから、 パターン変数にできます。 これは、添字付き関数を含む式の表現の結果です。

(もしクォートやフラグnoevalを通して抑制されないなら) 整理ルールは、 評価の後、適用されます。 tellsimpafterで確立されたルールは、 組み込みルールの後、それらが定義された順に適用されます。 ルールはボトムアップに適用されます。すなわち、 式全体への適用の前に、最初、部分式に適用されます。 ルールすべてが適用されることを保証するために、 (例えば、クォートクォート演算子''やフラグinfevalを介して) 結果を繰り返し整理する必要があるかもしれません。

パターン変数は、整理ルールの中でローカル変数として扱われます。 一旦ルールが定義されると、パターン変数の値は、 ルールに影響せず、ルールによって影響されません。 成功したルールマッチの結果となるパターン変数への割り当ては、 パターン変数の現在の割り当て(またはその欠落)に影響しません。 しかしながら、Maximaの中のアトムすべてで、 (putや関連関数で定義された)パターン変数のプロパティはグローバルです。

tellsimpafterによって構成されたルールは、 patternの主演算子に由来します。 組み込み演算子や infix, prefix, postfix, matchfix, nofixで 定義されたユーザー定義演算子に関するルールは、 Lisp識別子である名前を持ちます。 他の関数に関するルールは、Maxima識別子である名前を持ちます。

名詞と動詞形の扱いは少し混乱しています。 もしルールが名詞(または動詞)形に関して定義されて、 対応する動詞(または名詞)形に関するルールが既に存在しているなら、 新しく定義されたルールは両方の形式(名詞と動詞)に適用されます。 もし対応する動詞(名詞)形に関するルールが存在しないなら、 新しく定義されたルールは名詞(または動詞)形にだけ適用されます。

tellsimpafterで構成されたルールは通常のLisp関数です。 もしルール名が$foorule1なら、 構成子:lisp (trace $foorule1)は関数をトレースし、 :lisp (symbol-function '$foorule1)は定義を表示します。

tellsimpafterは引数をクォートします。 tellsimpafterは 新しく確立されたルールを含む、 patternの主演算子に関する ルールのリストを返します。

matchdeclare, defmatch, defrule, tellsimp, let, kill, remrule, clear_rulesも参照してください。

例:

patternは、 主演算子がパターン変数でない任意の非アトム式を取り得ます。

(%i1) matchdeclare (aa, atom, [ll, mm], listp, xx, true)$
(%i2) tellsimpafter (sin (ll), map (sin, ll));
(%o2)                 [sinrule1, simp-%sin]
(%i3) sin ([1/6, 1/4, 1/3, 1/2, 1]*%pi);
                    1  sqrt(2)  sqrt(3)
(%o3)              [-, -------, -------, 1, 0]
                    2     2        2
(%i4) tellsimpafter (ll^mm, map ("^", ll, mm));
(%o4)                  [^rule1, simpexpt]
(%i5) [a, b, c]^[1, 2, 3];
                                2   3
(%o5)                      [a, b , c ]
(%i6) tellsimpafter (foo (aa (xx)), aa (foo (xx)));
(%o6)                   [foorule1, false]
(%i7) foo (bar (u - v));
(%o7)                    bar(foo(u - v))

ルールはそれらが定義された順に適用されます。 もし2つのルールが式にマッチできるなら、 最初に定義されたルールが適用されます。

(%i1) matchdeclare (aa, integerp);
(%o1)                         done
(%i2) tellsimpafter (foo (aa), bar_1 (aa));
(%o2)                   [foorule1, false]
(%i3) tellsimpafter (foo (aa), bar_2 (aa));
(%o3)              [foorule2, foorule1, false]
(%i4) foo (42);
(%o4)                       bar_1(42)

整理ルールの中で、パターン変数はローカル変数として扱われます。 (defmatchと比較してください。 パターン変数をグローバル変数として扱います。)

(%i1) matchdeclare (aa, integerp, bb, atom);
(%o1)                         done
(%i2) tellsimpafter (foo(aa, bb), bar('aa=aa, 'bb=bb));
(%o2)                   [foorule1, false]
(%i3) bb: 12345;
(%o3)                         12345
(%i4) foo (42, %e);
(%o4)                 bar(aa = 42, bb = %e)
(%i5) bb;
(%o5)                         12345

アトムすべてには、 パターン変数のプロパティは、たとえ値がローカルでも、 グローバルです。 この例では、割り当てプロパティは define_variableを介して宣言されます。 Maximaの至る所で、これはアトムbbのプロパティです。

(%i1) matchdeclare (aa, integerp, bb, atom);
(%o1)                         done
(%i2) tellsimpafter (foo(aa, bb), bar('aa=aa, 'bb=bb));
(%o2)                   [foorule1, false]
(%i3) foo (42, %e);
(%o3)                 bar(aa = 42, bb = %e)
(%i4) define_variable (bb, true, boolean);
(%o4)                         true
(%i5) foo (42, %e);
Error: bb was declared mode boolean, has value: %e
 -- an error.  Quitting.  To debug this try debugmode(true);

ルールは主演算子に由来します。 組み込みやユーザー定義の演算子に関するルール名はLisp識別子で、 一方、他の関数に関する名前はMaxima識別子です。

(%i1) tellsimpafter (foo (%pi + %e), 3*%pi);
(%o1)                   [foorule1, false]
(%i2) tellsimpafter (foo (%pi * %e), 17*%e);
(%o2)              [foorule2, foorule1, false]
(%i3) tellsimpafter (foo (%i ^ %e), -42*%i);
(%o3)         [foorule3, foorule2, foorule1, false]
(%i4) tellsimpafter (foo (9) + foo (13), quux (22));
(%o4)                   [+rule1, simplus]
(%i5) tellsimpafter (foo (9) * foo (13), blurf (22));
(%o5)                  [*rule1, simptimes]
(%i6) tellsimpafter (foo (9) ^ foo (13), mumble (22));
(%o6)                  [^rule1, simpexpt]
(%i7) rules;
(%o7) [foorule1, foorule2, foorule3, +rule1, *rule1, ^rule1]
(%i8) foorule_name: first (%o1);
(%o8)                       foorule1
(%i9) plusrule_name: first (%o4);
(%o9)                        +rule1
(%i10) remrule (foo, foorule1);
(%o10)                         foo
(%i11) remrule ("^", ?\^rule1);
(%o11)                          ^
(%i12) rules;
(%o12)        [foorule2, foorule3, +rule1, *rule1]

加工された例: 反可換乗算。

(%i1) gt (i, j) := integerp(j) and i < j;
(%o1)           gt(i, j) := integerp(j) and i < j
(%i2) matchdeclare (i, integerp, j, gt(i));
(%o2)                         done
(%i3) tellsimpafter (s[i]^^2, 1);
(%o3)                 [^^rule1, simpncexpt]
(%i4) tellsimpafter (s[i] . s[j], -s[j] . s[i]);
(%o4)                   [.rule1, simpnct]
(%i5) s[1] . (s[1] + s[2]);
(%o5)                    s  . (s  + s )
                          1     2    1
(%i6) expand (%);
(%o6)                      1 - s  . s
                                2    1
(%i7) factor (expand (sum (s[i], i, 0, 9)^^5));
(%o7) 100 (s  + s  + s  + s  + s  + s  + s  + s  + s  + s )
            9    8    7    6    5    4    3    2    1    0
関数: clear_rules ()

kill (rules)を実行し、 足し算+, 掛け算*, べき^に関して 次のルール番号を1に再設定します。


Next: , Previous:   [Contents][Index]