2

Perlで任意のPerlプロシージャ(coderefsによって取得)のコードを検査して操作したいと思います。そのためのツール/モジュール/ライブラリはありますか?B :: Conciseに似ていますが、B :: Conciseが出力にコードを出力する点が異なりますが、プログラムで検査したいと思います。

こんな感じで使いたいです。Fたとえばと呼ばれるcoderefが与えられます。10個の引数付き:

$ret = &$F(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);

F1関数stを作成したいのですが。

&$F(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) == 
  &$F1(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)*
  &$C(x2, x3, x4, x5, x6, x7, x8, x9, x10)

つまり、2つの部分に「因数分解」します。2つ目は依存せずx1、1つ目は可能な限り単純です(私はF巨大な製品として構築されていると思います)。

これが必要なアプリケーションは、メトロポリスサンプリングアルゴリズムの最適化です。分布をサンプリングしていると仮定しますp(x1 | x2 = X1, x3 = X3, ...) = f(x1, x2, x3, ...)。アルゴリズム自体は不変です。乗法定数係数やその他の変数はアルゴリズムによって変化しないため、依存しない部分x1(つまり$c上から)を評価する必要はまったくありません)。

同時確率は、例えばを持っているかもしれません。次のフォーム:

  p(x1, x2, x3, x4, x5) = g1(x1, x2)*g2(x2, x3)*g3(x3, x4)*g4(x4, x5)*g5(x4, x1)*g6(x5, x1)

pまた、特定の因子が依存する変数の注釈が付いた因子で構成されるオブジェクトとして構築することも検討します。これでも、コードのイントロスペクション(変数を自動的に決定する)の恩恵を受けます。

4

3 に答える 3

9

optreeのイントロスペクションにBは、通常、モジュールのファミリーが使用されます。

コード参照を指定して$cv、最初にそのためのBオブジェクトを作成します。

my $b_cv = B::svref_2object($cv);

これで、そこに記載されているさまざまなメソッドを呼び出してB、オプツリーからさまざまなものを取得できます。

optreeイントロスペクションのみを使用すると、すでに驚くべきことを達成できます。DBIx::Perlishこのかなり高度な例については、を参照してください。

また、B::Generateモジュールがあり、これを使用して、必要な処理を実行する新しいoptreeを構築したり、既存のoptreeを操作したりできます。ただし、B::Generate期待するほど成熟しておらず、多くの不足している機能とかなりの数のバグがあります。

実際のオプツリーの作成と操作は、通常、、、、などで文書化されているようにperlapi、perlのCapiを使用して行うのが最適です。おそらく、書き戻したoptree操作関数をperlスペースに公開するために、いくつかのことも学ぶ必要がありますが、それは本当に簡単な部分です。perlgutsperlhackXS

Syntax Plugins特にperl5.12.0でコアに追加されて以来、オプツリーの構築(必ずしもイントロスペクトされている他の既存のオプツリーに基づく必要はありません)が最近やや人気になっているようです。Scope::Escape::Sugarあなたはcpanのような様々な例を見つけることができます。

ただし、perlのオプツリーの処理はまだやや面倒で、初心者向けではありません。最も難解なことには必要ないはずです。評価されたソースコードを使用してB::Deparse->new->coderef2text($cv)、それからほんの少しだけマングリングするようなものは、純粋なperlスペースからのoptreeイントロスペクションで行きたいと思う限りです。

少し前に戻って、解決しようとしている実際の問題を説明することをお勧めします。たぶん、オプツリーをまったくいじることを伴わない、はるかに単純な解決策があるでしょう。

于 2010-10-05T00:32:08.743 に答える
1

言い直した質問を考えると、coderefを変更しようとするのではなく、ここで何をすべきかは、coderefの使用を可能な限り遅らせることだと思います。

  1. 計算のインスタンスを表すオブジェクトを作成します。
  2. 計算の値を評価するために必要なこのオブジェクトのメソッドを記述します。codegenはありません、ただそれを長く遅い方法で行ってください。これは、簡単にテストでき、うまくいけば簡単に理解できる次のステップのコードのベースラインを提供するためのものです。
  3. ステップ2で行ったことの正確さを確認するためのテストを作成します(そのような人の場合は、ステップ2の前にこれを交換してください)。
  4. 計算オブジェクトを、同じ計算のより最適化された形式を表す新しいオブジェクトに変換するメソッドを作成することにより、この質問で質問していることを実装します。テストを使用して、最適化後も計算で正しい結果が得られることを確認します。
  5. 計算オブジェクトを受け取り、その計算を実行するsubを(文字列evalまたは使用してB)生成するコードを記述します。テストを使用して、コンパイル後も計算で正しい結果が得られることを確認します。

2〜5の任意の場所に挿入するオプションの手順:

  • overloadたくさんのオブジェクトコンストラクターの代わりに、計算自体に似た見栄えの良い式を使用して「計算オブジェクト」を構築できるように、構文糖衣構文を記述します(おそらく、を使用しますが、他のツールも可能です)。
于 2010-10-05T06:59:07.563 に答える
0

Perl 5では、そのようにバイトコードをその場で操作することはできませんが、無名関数を作成することはできます。私があなたの例を正しく理解していて、私がそうは思わない場合、あなたはすでにとによって参照されている2つの関数を持っており$f1、最初の2つの結果を互いに乗算した結果を保持$cする新しい参照を作成したいとします。$fこれは簡単です:

my $f = sub { $f1->(@_) * $c->(@_[1 .. 9]) };

$f->(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

&coderefを逆参照するのではなく、矢印演算子を使用していることに注意してください。このスタイルははるかに一般的です(そして私の意見ではより読みやすくなっています)。

于 2010-10-05T00:23:42.690 に答える