我想在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 sampling algorithm的优化 - 假设我正在对分布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
构建为一个对象,该对象由具有特定因子所依赖的变量的注释的因子组成。即使这样也会受益于代码内省(自动确定变量)。
答案 0 :(得分:9)
为了反省内容,通常会使用B
系列模块。
给定代码引用$cv
,首先为其创建一个B
对象:
my $b_cv = B::svref_2object($cv);
现在,您可以调用B
中记录的各种方法来从optree中检索各种内容。
仅使用optree内省,您已经可以实现惊人的功能。有关此问题的一个非常高级的示例,请参阅DBIx::Perlish
。
还有一个B::Generate
模块,允许建立新的选择,无论你想做什么,或操纵现有的选择。但是,B::Generate
并不像人们希望的那样成熟,并且有很多缺失的功能和相当多的错误。
实际的optree创建和操作通常最好使用perl的C api完成,如perlapi
,perlguts
和perlhack
中所述。你可能还需要学习一些XS
,以便将你写回的perree操作函数暴露给perl空间,但这真的很容易。
建立optrees(不一定基于其他现有的自我反馈)似乎最近变得有些流行,特别是因为Syntax Plugins
已添加到perl 5.12.0的核心中。您可以在cpan上找到各种示例,例如Scope::Escape::Sugar
。
然而,处理perl的选择仍然有点繁琐,并不完全适合初学者。它不应该是任何最神秘的东西。使用B::Deparse->new->coderef2text($cv)
之类的东西,然后使用评估的源代码稍微修改一下,就像我想要从纯perl空间进行optree内省一样。
您可能想稍微退一步并解释您尝试解决的实际问题。也许有一个更简单的解决方案,根本不涉及搞乱选择。
答案 1 :(得分:1)
考虑到你重申的问题 - 我认为你应该在这里做的,而不是尝试munge coderefs,就是尽可能延迟使用coderef。
eval
或使用B
)。使用您的测试来确保计算在编译后仍能提供正确的结果。可插入2到5之间任意位置的可选步骤:
overload
,但也可以使用其他工具)让你使用类似于计算本身的漂亮表达式来构造“计算对象”,而不是大量的对象构造函数答案 2 :(得分:0)
Perl 5不允许你像这样动态操作字节码,但你可以创建匿名函数。如果我正确理解您的示例,并且我怀疑我这样做,那么您已经有$f1
和$c
引用的两个函数,并且您想要创建一个新的引用$f
前两个的结果相互乘以。这很简单:
my $f = sub { $f1->(@_) * $c->(@_[1 .. 9]) };
$f->(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
请注意,使用箭头运算符而不是&
来取消引用coderef。这种风格更为常见(在我看来更具可读性)。