如何内联Perl子例程?

11

我正在阅读代码大全2,其中提到了即使针对那些似乎过于简单的操作也要创建子程序,以及如何有所帮助。

我知道可以使用inline关键字在C和C ++中内联函数。但我从未遇到过在Perl中内联子例程的方法。

是否有一种方法可以告诉Perl解释器内联子例程调用(或为什么不)?


即使在 Perl 中内联的机会似乎更有限,您仍应遵循这些建议。 - Sinan Ünür
4个回答

25

常量子程序,即带有空原型和常量返回值的子程序是内联的。这就是constant编译指示符定义常量的方式:

sub five() { 5 }

如果在第一次使用之前看到它,它将被内联。
否则,Perl允许在运行时动态重新定义子例程,因此内联不适用。
对于始终以相同值返回相同输入的子例程,可以使用memoizationProgramming Perl的第13章提供了有关perl执行的优化步骤的一些信息。

这称为常量折叠。常量折叠不仅限于将2 ** 10在编译时转换为1024等简单情况。它还解析函数调用 - 内置和符合第6章“内联常量函数”中条件的用户声明的子例程。与FORTRAN编译器对其自身固有功能的臭名昭著的了解类似,Perl也知道在编译期间调用其自己的内置函数。这就是为什么如果您尝试取log(0.0)或sqrt负常量,您会遇到编译错误而不是运行时错误,并且解释器根本没有运行。

另请参见perldoc perlguts
您可以自己看到常量折叠的效果:
#!/usr/bin/perl

use strict; use warnings;

sub log_ok () { 1 }

if ( log_ok ) {
    warn "log ok\n";
}
perl -MO=Deparse t.pl

输出:

sub log_ok () { 1 }
use warnings;
use strict 'refs';
do {
    warn "log ok\n"
};
t.pl 语法 OK

在这里,常量折叠导致if块被替换为do块,因为编译器知道log_ok总是返回真值。另一方面,对于:

#!/usr/bin/perl

use strict; use warnings;

sub log_ok () { 0.5 > rand }

if ( log_ok ) {
    warn "log ok\n";
}

Deparse输出:

sub log_ok(){
    use warnings;
    use strict 'refs';
    0.5 > rand;
}
use warnings;
use strict 'refs';
if(log_ok){
    warn "日志正常\n";
}
t.pl语法OK

C编译器可能已将if(log_ok)替换为if(0.5> rand)perl不会这样做。


5

Perl只允许内联常量函数。来自perldoc perlsub

带有原型()的函数是内联的潜在候选项。如果经过优化和常量折叠后的结果是一个常量或者没有其他引用的词法作用域标量,则它将被用于没有 & 的函数调用的位置。


4

我没有尝试过这些方法,但如果你有时间,可以尝试以下方法:

  1. Macro
  2. macro
  3. 或者Filter::Macro

它们都是源代码过滤器,因此您需要检查性能的回报率。 最后一个在cpanratings上有一篇评论。(忽略Dan Dascalescu对Perl模块“airspace”的尝试规范。)

-- 实际上,最后一个Filter::Macro使用Filter::Simple::Compile(进而使用Module::Compile)来编译例程,因此这个方法可能比其他源代码过滤器表现更好。但标准的源代码过滤器警告仍然适用。


1
写Perl的时候,速度可能不应该是一个考虑因素。可以放心地将事物封装为函数。如果后来的分析显示你在一个简单函数中花费了很多时间,那么可以自己将该函数内联。

"过早优化是万恶之源",唐纳德·科努斯如此说道,他说得对。 - Jinxed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接