如何从 Perl 中的 coderef 获取语法树?
我想在 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
构造为一个由因素组成的对象,并带有特定因素所依赖的变量的注释。即使这样也会受益于代码自省(自动确定变量)。
I'd like to inspect and manipulate code of arbitrary Perl procedures (got by coderefs) in Perl. Is there a tool/module/library for that? Something similar to B::Concise, except that B::Concise prints the code on output, but I'd like to inspect it programmatically.
I'd like to use it like this. Given a coderef F
, which is called eg. with 10 arguments:
$ret = &$F(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);
I'd like to create a function 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)
that is to "factor" it into two parts, where the second doesn't depend on x1
and the first is as simple as possible (I assume F
is constructed as a huge product).
The application I want this for is optimization of Metropolis sampling algorithm - suppose I'm sampling the distribution p(x1 | x2 = X1, x3 = X3, ...) = f(x1, x2, x3, ...)
. The algorithm itself is invariant wrt. multiplicative constant factors, and other variables do not change through the algorithms, so the part not depending on x1
(ie. $c
from above) need not be evaluated at all).
The joint probability might have eg. the following form:
p(x1, x2, x3, x4, x5) = g1(x1, x2)*g2(x2, x3)*g3(x3, x4)*g4(x4, x5)*g5(x4, x1)*g6(x5, x1)
I also consider constructing p
as an object consisting of the factors with annotations of which variables does a particular factor depend on. Even this would benefit from code introspection (determining the variables automatically).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
对于 opttree 的自省,通常使用
B
系列模块。给定代码引用
$cv
,首先为此创建一个B
对象:现在您可以调用
B
用于从 optree 检索各种内容。仅使用 Optree 内省,您就已经可以实现令人惊奇的事情。请参阅
DBIx::Perlish
了解这方面的高级示例。还有一个
B::Generate
模块,允许构建新的 opttree 可以做任何你想做的事情,或者操纵现有的 opttree。然而,B::Generate
并不像人们希望的那样成熟,并且存在许多缺失的功能和相当多的错误。实际的 opttree 创建和操作通常最好使用 perl 的 C api 来完成,如
perlapi
、perlguts
和perlhack
等等。您可能还需要学习一些XS
,以公开您写回perl的optree操作函数空间,但这确实是最简单的部分。构建 opttree(不一定基于正在内省的其他现有 opttree)最近似乎变得有些流行,特别是自从
Syntax Plugins
已添加到 perl 5.12.0 的核心中之后。您可以找到各种示例,例如Scope::Escape::Sugar
在 cpan 上。然而,处理 Perl 的 opttree 仍然有些繁琐,而且不太适合初学者。对于任何最神秘的事情来说都没有必要。像使用
B::Deparse->new->coderef2text($cv)
这样的东西,然后可能对评估的源代码进行轻微的修改,这确实是我想要进行 optree 内省的事情来自纯 Perl 空间。您可能需要退后一步并解释您要解决的实际问题。也许有一个更简单的解决方案,根本不涉及扰乱 opttree。
For introspection of optrees the
B
family of modules is usually used.Given an code reference
$cv
, first create aB
object for that:Now you can call the various methods documented in
B
on that to retrieve various things from the optree.Using only optree introspection you can already achieve amazing things. See
DBIx::Perlish
for a pretty advanced example of this.There also happens to be a
B::Generate
module, that allows building new optrees that do whatever you want, or to manipulate existing optrees. However,B::Generate
isn't as mature as one would hope, and there's a lot of missing features and quite a few bugs.Actual optree creation and manipulation is usually best done using perl's C api, as documented in
perlapi
,perlguts
, andperlhack
, among others. You'll probably have to learn someXS
as well, to expose the optree manipulation functions you wrote back to perl space, but that's the easy part really.Building optrees (not necessarily based on other existing optrees that are being introspected) seems to have become somewhat popular recently, especially since
Syntax Plugins
have been added to the core in perl 5.12.0. You can find various examples likeScope::Escape::Sugar
on cpan.However, dealing with perl's optrees is still somewhat fiddly and not exactly beginner-friendly. It shouldn't be necessary for any of the most arcane things. Something like using
B::Deparse->new->coderef2text($cv)
and then maybe mangling very slightly with the evaluated source code is really as far as I would want to go with optree introspection from pure-perl space.You might want to step back a bit and explain the actual problem you're trying to solve. Maybe there's a much simpler solution that doesn't involve messing with optrees at all.
鉴于您重申的问题 - 我认为您应该在这里做的,而不是尝试修改 coderefs,而是尽可能长时间地延迟使用 coderef。
插入 2 到 5 之间任意位置的可选步骤:
Given your restated question -- I think what you should do here, instead of trying to munge coderefs, is to delay having a coderef as long as possible.
eval
or usingB
) that carries out that computation. Use your tests to ensure that computations still give the right result after they've been compiled.Optional step to insert anywhere between 2 and 5:
overload
, but other tools are possible too) to let you construct "computation objects" using nice-looking expressions that resemble the computation itself, instead of lots and lots of object constructors.Perl 5 不允许您像这样动态操作字节码,但您可以创建匿名函数。如果我正确理解你的示例,并且我怀疑我理解正确,那么你已经有两个被
$f1
和$c
引用的函数,并且你想创建一个新的引用$f
保存前两个相乘的结果。这很简单:请注意使用箭头运算符而不是
&
来取消引用 coderef。这种风格更常见(并且在我看来更具可读性)。Perl 5 does not let you manipulate the bytecode on the fly like that, but you can create anonymous functions. If I understand your example correctly, and I doubt I do, you already have two functions that are being referenced by
$f1
and$c
, and you want to create a new reference$f
that holds the results of the first two multiplied by each other. This is simple:Note the use of the arrow operator rather than the
&
to dereference the coderefs. This style is much more common (and in my opinion more readable).