2

我想在 Perl 中检查和操作任意 Perl 过程的代码(由 coderefs 获得)。是否有用于此的工具/模块/库?类似于B::Concise的东西,除了 B::Concise 在输出上打印代码,但我想以编程方式检查它。

我想像这样使用它。给定一个 coderef F,它被称为例如。有 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)

那就是将它“分解”成两部分,第二部分不依赖,x1第一部分尽可能简单(我假设F它是一个巨大的产品)。

我想要的应用程序是Metropolis 采样算法的优化- 假设我正在对分布进行采样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 中检索各种内容。

仅使用 optree 内省,您已经可以实现令人惊奇的事情。参见DBIx::Perlish一个非常高级的例子。

还有一个B::Generate模块,它允许构建新的 optree 来做任何你想做的事情,或者操纵现有的 optree。然而,B::Generate它并不像人们希望的那样成熟,并且有很多缺失的功能和相当多的错误。

实际的 optree 创建和操作通常最好使用 perl 的 C api 完成,如perlapiperlgutsperlhack等中所述。您可能还需要学习一些XS知识,以将您写回 perl 空间的 optree 操作函数公开,但这确实是简单的部分。

构建 optrees(不一定基于正在自省的其他现有 optrees)最近似乎变得有些流行,尤其是Syntax Plugins在 perl 5.12.0 中添加到核心之后。您可以Scope::Escape::Sugar在 cpan 上找到各种示例。

然而,处理 perl 的 opttree 仍然有些繁琐,而且对初学者并不友好。对于任何最神秘的事情,它都不应该是必需的。像使用B::Deparse->new->coderef2text($cv)评估的源代码然后可能会稍微修改一些东西,这实际上是我想要从纯 perl 空间进行 optree 内省的方式。

您可能想退后一步,解释一下您要解决的实际问题。也许有一个更简单的解决方案,根本不涉及弄乱 optrees。

于 2010-10-05T00:32:08.743 回答
1

鉴于您重述的问题-我认为您应该在这里做的,而不是尝试使用 coderef,而是尽可能长时间地延迟使用 coderef。

  1. 创建一个表示计算实例的对象。
  2. 在此对象上编写评估计算值所需的方法。没有代码生成器,只需以缓慢的方式进行即可。这只是为您提供易于测试并希望易于理解的后续步骤的代码基线。
  3. 编写测试以确保您在第 2 步中所做的事情的正确性。(如果您是那种人,请在第 2 步之前交换此内容。)
  4. 通过编写将计算对象转换为表示同一计算的更优化形式的新对象的方法,实现您在此问题中所问的内容。使用您的测试来确保计算在优化后仍然给出正确的结果。
  5. 编写接受计算对象的代码,并生成执行该计算的子程序(无论是通过字符串eval还是使用B)。使用您的测试来确保计算在编译后仍然给出正确的结果。

插入 2 到 5 之间任意位置的可选步骤:

  • 编写一些语法糖(可能使用overload,但也可以使用其他工具),让您使用类似于计算本身的漂亮表达式来构造“计算对象”,而不是大量的对象构造函数。
于 2010-10-05T06:59:07.563 回答
0

Perl 5 不允许您像那样动态地操作字节码,但您可以创建匿名函数。如果我正确理解了您的示例,并且我怀疑我没有正确理解,那么您已经有两个被$f1and引用的函数$c,并且您想要创建一个新的引用$f来保存前两个的结果相乘。这很简单:

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

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

请注意使用箭头运算符而不是&取消引用 coderefs。这种风格更常见(在我看来更具可读性)。

于 2010-10-05T00:23:42.690 回答