Location : Home > Languages > Perl > Package Title : Math::Symbolic::Custom::Transformation |
![]() |
Math::Symbolic::Custom::Transformation - Math::Symbolic ツリーの変換
use Math::Symbolic::Custom::Transformation; my $trafo = Math::Symbolic::Custom::Transformation->new( 'TREE_x + TREE_x' => '2 * TREE_x' ); my $modified = $trafo->apply($math_symbolic_tree); if (defined $modified) { print "Outermost operator is a sum of two identical trees.\n"; print "Transformed it into a product. ($modified)\n"; } else { print "Transformation could not be applied.\n"; } # ショートカット: new_trafo use Math::Symbolic::Custom::Transformation qw/new_trafo/; # 置換後に式の値を変換計算するために value() 関数を用いる。 # simplify{} は同様な動きをする。 my $another_trafo = new_trafo( 'TREE_foo / CONST_bar' => 'value{1/CONST_bar} * TREE_foo' ); # 同じ変換をしたいがオブジェクトに保存したくない場合には以下のようにすればよい。 use Memoize; memoize('new_trafo'); # Then, 同じ変換文字列では、比較の文字列を完全に再生成するのに比べて(私のマシンで)130%スピードアップしている。既存の変換を用いるより20%ほど遅いだけである。
Math::Symbolic::Custom::Transformation は Math::Symbolic モジュールへの拡張である。ユーザはドキュメント全体を通してそのモジュールに親しんでいないものとする。
本パッケージは Math::Symbolic ツリーを用いることで Math::Symbolic ツリーの変換を実装している。以下のパラグラフでこれが何を意味するかを説明しよう。
今までは Math::Symbolic ツリーを探索するには、(T_OPERATOR のような)定数とともに最上段のノードタイプと多くの定数との比較を行うような低水準の Math::Symbolic インタフェースを利用する必要があった。Math::Symbolic::Custom::Pattern のリリースによって状況は変化した。
ツリーを変更するためには、ユーザは等しく低水準またはカプセルを破壊するような方法が必要であった。これが今回のディストリビューションによる変化である。
さて、2つの個別のツリーを1つのツリーにすることを想定してみよう。元オブジェクトは変数 $tree にあるとしよう。古い方法では(厳密には警告が発生するが)以下のようになる。
use Math::Symbolic qw/:all/; sub sum_to_product { if ( $tree->term_type() == T_OPERATOR and $tree->type() == B_SUM and $tree->op1()->is_identical($tree->op2()) ) { $tree = Math::Symbolic::Operator->new( '*', Math::Symbolic::Constant->new(2), $tree->op1()->new() ); } return $tree; }
本パッケージではなすべきことは人間に可読になっている。
use Math::Symbolic::Custom::Transformation qw/new_trafo/; my $Sum_To_Product_Rule = new_trafo('TREE_a + TREE_a' => '2 * TREE_a'); sub sum_to_product { my $tree = shift; return( $Sum_To_Product_Rule->apply($tree) || $tree ); }
もちろんどちらのバージョンも短くすることができる。しかし重要な改善はこの例で示されているものではない。最も外部の演算子を超えて内観を行いたければそれを巨大にして終わらせることもできる。古いスタイルの変換を用いて if-else 節で記述することは難しいが。 しかしこのパッケージではそのような内観は縮小拡大できる。
use Math::Symbolic::Custom::Transformation qw/new_trafo/; my $Sum_Of_Const_Products_Rule = new_trafo( 'CONST_a * TREE_b + CONST_c * TREE_b' => 'value{CONST_a + CONST_c} * TREE_b' ); sub sum_to_product { my $tree = shift; return( $Sum_Of_Const_Products_Rule->apply($tree) || $tree ); }
変換文字列における value{} コンストラクトに関する詳細は、文法的拡張をセクションを参照のこと。
本モジュールでは何もエクスポートしない。しかし Math::Symbolic::Custom::Transformation オブジェクトのコンストラクタの代替として new_trafo サブルーチンを選択することができる。
変換のパフォーマンスはそれ自身では驚異的なものではない。しかし元のツリーを無傷のままおいておくことを考慮すると、文字通りのコードに比べ16%速くなっている。(巨大な if-else 節を考慮している。)
必要な時には文字列から変換オブジェクトを再生成することもできる。これについて言えることはただ1つ。
やるな!
アプリケーションの生成ではなくパフォーマンスに最適化されているために、変換の構築は実に遅い。(アプリケーションは文字列の生成に比べて40倍も速い!)
もし変換が用いられるソース文字テルを含まねばならないのならs used, consider using 標準的な Perl ヒストリビューションに含まれている Memoize モジュールを使うことを検討すること。
use Memoize; use Math::Symbolic::Custom::Transformation qw/new_trafo/; memoize('new_trafo'); sub apply_some_trafo { my $source = shift; my $trafo = new_trafo(...some pattern... => ...some transformation...); return $trafo->apply($source); }
この使用法は可読性の上からは最も意味のあるソース文字列の変換に長所がある。メモイズされたサブルーチン new_trafo は呼び出されたときに最初に変換を構築し、それ以降はキャッシュされたオブジェクトを返す。
変換から生成される文字列は基本的には Math::Symbolic ツリーとしてパースすることができる。実際、変換コンストラクタの最初の引数は Math::Symbolic::Custom::Pattern オブジェクトとしてパースされる。しかし2つ目の引数はデフォルトの Math::Symbolic 文法の拡張を含んでいるかも知れない。これらの拡張は2つの関数 value{...} 及び simplify{...} である。他の代数的括弧と区別するために波括弧(curly braces;{ })を用いている。value{EXPR} 命令を見つけるとモジュールは変換が行われたときに EXPR の値を計算する。(すなわち TREE_foo, CONST_bar, VAR_baz の後に代替物が挿入される!) 結果は変換されたツリーに挿入される。
同様に simplify{EXPR} 命令は will 変換がじっこうされたときに EXPR に関する Math::Symbolic 単純化ルーチンを呼び出す。(そして代替物がマッチしたサブツリーと入れ替える)
以下はパブリックメソッドのリストである。
new
これは Math::Symbolic::Custom::Transformation オブジェクトへのコンストラクタである。探索すべきパターンとその置換の2つの引数をとる。
パターンは、Math::Symbolic::Custom::Pattern オブジェクト(最速である)または内部的にパターンに変換される Math::Symbolic ツリーまたはパターンとしてパースされる文字列であればよい。
パターンへの置換は Math::Symbolic ツリーまたはそのようにパースされる文字列でよい。
apply
Math::Symbolic ツリーへの変換を行う。最初の引数は変換される Math::Symbolic ツリーでなければならない。ツリーはそのまま変換されるのではなく、マッチしたサブツリーは変換されたツリーに含まれる。したがって変換されたツリーと同じく元のツリーも利用したい場合はツリーのコピーをとっておく必要がある。
apply() は変換パターンが待ちすればその変換されたツリーを返し、そうでなければ false を返す。
エラーの場合はフェイタルエラーを返す。
以下はパブリックサブルーチンのリストである。
new_trafo
本サブルーチンは、パッケージ名がコードに直接記述された Math::Symbolic::Custom::Transformation オブジェクトの new() コンストラクタの代替である。(だからもしユーザが本モジュールをサブクラスとして利用するならばそのことに注意すること!)
本モジュールの最新のバージョンは http://steffen-mueller.net または CPAN で入手可能である。
Math::Symbolic
本モジュールは記号演算のフレームワークとして Math::Symbolic を利用している。
パターンマッチングルーチンとして Math::Symbolic::Custom::Pattern を利用している。
Steffen Muler, symbolic-module at steffen-mueller dot net
Copyright (C) 2006 by Steffen Muler
本ライブラリはフリーソフトウェアであり、Perl 本体と同等の条件で修正/再配布してもよい。Perl 5.6.1、利用者の選択によっては Perl 5以降の入手可能なバージョンで利用可能である。
![]() |
Updated : 2007/07/04 |