据我所知,在Perl中,我们可以使用这些技术从模块调用子例程:
foo
,导入具有此子例程的模块。最后在你的perl脚本中调用它。Object
,最后使用foo
调用Object
。foo
,例如myDir::Module::foo();
。如果我总是感到困惑,这是调用子程序foo
的更好方法。
如果我有一个动态脚本,我从浏览器而不是命令行运行,那么应采用哪种方法,以便脚本花费更少的时间。
感谢。
答案 0 :(得分:6)
在Perl中调用代码的最快和最好的方法之间存在差异。
编辑:请参阅simbabques回答。他特别介绍了#1和#3之间的差异,以及为什么要使用它们。
您的#1和#3是相同的:子例程在全局可见的命名空间中具有唯一的名称。许多名称可以通过别名映射到一个子例程,或者导入模块。
如果在编译时已知要调用的函数的名称,则将在编译时解析该子函数。这假设您不会自发地重新定义您的功能。如果确切的函数仅在运行时知道,那么这只是哈希查找。
有三种方法可以调用函数:
foo(@args);
&foo(@args);
@_ = @args; goto &foo;
第一个(大括号有时是可选的)是默认的,并且针对子原型验证你的参数(不要使用原型)。此外,构造了整个调用堆栈帧(具有许多有用的调试信息)。这需要时间。
第二个跳过原型验证,并假设您知道自己在做什么。这稍快一点。我认为这是草率的风格。
三号是尾调用。这将从当前sub返回,返回值为foo
。这很快,因为原型被忽略,并且可以重用当前的调用堆栈帧。这经常没用,而且语法很难看。内联代码的速度提高了一个数量级(即在Perl中,我们更喜欢循环而不是递归。)。
OO的灵活性是以极高的性价格出现的:由于您调用消息的对象类型在运行时才知道,实际方法只能在运行时解析。
这意味着$foo->bar()
会在bar
被$foo
编辑的包中查找函数bless
。如果在那里找不到它,它将在父类中搜索。这很慢。如果要使用OO,请注意浅层次结构(→减少查找次数)。还要注意Perls默认的方法解析顺序是不寻常的。
即使您知道类型,也不能减少对函数调用的方法调用。
如果$foo
属于Foo
类,而Foo::bar
是子类,则Foo::bar($foo)
将跳过方法结果,甚至可能有效。但是,这会破坏封装,并且会在Foo
子类化后中断。此外,如果Foo
未定义bar
,则此方法无效,但该方法是在父类中定义的。
我一般都支持面向对象,直到从基准测试中可以看出这不会提供您所需的性能。
答案 1 :(得分:4)
- 导出子程序foo,导入具有该子程序的模块。最后在你的perl脚本中调用它。
为了做到这一点,你可以在实现sub的模块/ package
中使用Exporter。您可以通过@EXPORT_OK
和@EXPORT
告诉您的模块将导出哪些内容。如果你use
模块,那么东西会在编译时导入到你当前的命名空间。以下语句是等效的。
# This is the same...
use Module;
# ... as this
BEGIN {
require Module;
Module->import();
}
如果你有要在主脚本中使用的东西,或者你打算经常使用,你想要这样做。一些示例包括List::Util,Data::Dumper或use feature 'say'
。当然,您也可以在其他模块中使用它。
use Data::Dumper;
use List::Util qw(max);
use feature qw(say);
my @foo = (1, 2, 3, 4, 5, 23);
print Dumper \@foo;
say max(@foo);
问题是,在这里,你污染了'您的命名空间如果必须,请执行此操作,但请记住它在编译时发生,因此不是条件。你不能说:
if ($foo) {
use Some::Module 'foo';
foo($foo);
} else {
use Something::Else 'bar';
bar();
}
它会在编译时加载Some::Module
和 Something::Else
,从而增加程序消耗的时间和内存。这种情况当然会奏效,但效率不高。
- 在perl脚本中创建该模块的Object,最后使用该Object调用foo。
这是OOp方法。它(如上所述)不能与其他方法相比。您不需要导入对象的方法。您只需使用use
或require
(参见上文)加载您的类(这是一个模块),创建一个实例并根据自己的喜好使用它的方法。但是,您需要一个面向对象的模块。如果您对如何运作感兴趣,请先查看perlootut。
- 使用其路径直接调用foo,如myDir :: Module :: foo();.
它实际上不是它的路径,而是它的名称(空间)。例如,Data :: Dumper Dumper.pm
位于Data
目录中的lib
文件夹中。但这并不重要。
与第一种方法的主要区别在于您省略了导入部分。如果您想要构建有条件地加载某些模块的东西,或者如果您在一个巨大的(可能是遗留的)应用程序中并且不想污染命名空间,这将非常有用。
if ($order_has_some_condition) {
require Very::Long::NameSpace::For::This::Condition::Module;
Very::Long::NameSpace::For::This::Condition::Module::do_stuff_with_an_order($order);
}
想象一下,这段代码是在一个遗留的子代码中,有2k行并且有很多东西正在进行中,大多数代码在我们的案例中从未被调用过。我们不希望use
我们的模块,使其可用于这一大块代码中处理的100种不同案例中的每一种。相反,我们只想加载它,如果我们真的需要它。现在我们require
模块并使用全名直接调用它。
在结论中,第一种和第三种方式都有其优点。它们都需要存在,并且如果合适,它们都应该被使用。在某些情况下,它只是味道,但在其他情况下,它是有道理的决定。第二种,OOp方法完全不同。
没有真正的速度差异,正如Borodin所说,Perl很快。当然,如果你没有import
的东西,你就不必付钱了。进口。在一个10行的剧本中,这并不重要。在遗留软件中,可能存在数千行代码和一个巨大文件中的许多用例,这很重要。
我希望这有助于你做出决定。