不幸的是,在 Perl 中创建包、导出等方面,我完全是个菜鸟。我尝试阅读一些模块,并且经常发现自己从冗长的章节中打瞌睡。如果我可以在一个简单的网页中找到我需要理解的内容而无需向下滚动,那将会很有帮助。:P
基本上我有两个模块,A 和 B,A 将使用 B 中的一些函数,B 将使用 A 中的一些函数。当我尝试通过perl -c
.
有没有办法正确地做到这一点?还是我的设计迟缓了?如果是这样,什么是更好的方法?因为我这样做的原因是避免复制 n 将其他模块功能再次粘贴到该模块中并重命名它们。
不幸的是,在 Perl 中创建包、导出等方面,我完全是个菜鸟。我尝试阅读一些模块,并且经常发现自己从冗长的章节中打瞌睡。如果我可以在一个简单的网页中找到我需要理解的内容而无需向下滚动,那将会很有帮助。:P
基本上我有两个模块,A 和 B,A 将使用 B 中的一些函数,B 将使用 A 中的一些函数。当我尝试通过perl -c
.
有没有办法正确地做到这一点?还是我的设计迟缓了?如果是这样,什么是更好的方法?因为我这样做的原因是避免复制 n 将其他模块功能再次粘贴到该模块中并重命名它们。
具有循环依赖关系并不是一个很好的做法。我建议将某些东西或其他东西分解到第三个模块,这样你就可以让 A 依赖于 B,A 依赖于 C,B 依赖于 C。
所以......将公共代码分解到另一个模块中的建议是一个很好的建议。但是,您不应该将模块命名为 *.pl,也不应该通过require
-ing 某个路径名来加载它们(如在 中require
"../lib/foo.pl";
)。(一方面,说 '..' 使您的脚本每次都依赖于从同一个工作目录执行。因此,当您将脚本运行为 时,您的脚本可能会工作,但当您将其运行为时perl foo.pl
,它将无法工作perl YourApp/foo.pl
。那就是一般不好。)
假设您的应用名为 YourApp。您应该将应用程序构建为一组位于lib/
目录中的模块。例如,这是一个“Foo”模块;它的文件名是lib/YourApp/Foo.pm
.
package YourApp::Foo;
use strict;
sub do_something {
# code goes here
}
现在,假设您有一个名为“Bar”的模块,它依赖于“Foo”。您只需制作lib/YourApp/Bar.pm
并说:
package YourApp::Bar;
use strict;
use YourApp::Foo;
sub do_something_else {
return YourApp::Foo::do_something() + 1;
}
(作为一个高级练习,您可以在使用包的命名空间中使用Sub::Exporter
或Exporter
制作use YourApp::Foo
install 子例程,这样您就不必YourApp::Foo::
在一切之前编写。)
无论如何,你像这样构建你的整个应用程序。功能的逻辑部分应该组合在模块(甚至更好的类)中。
为了让这一切运行起来,你编写了一个看起来像这样的小脚本(我把它们放进去bin/
,所以我们称之为bin/yourapp.pl
):
#!/usr/bin/env perl
use strict;
use warnings;
use feature ':5.10';
use FindBin qw($Bin);
use lib "$Bin/../lib";
use YourApp;
YourApp::run(@ARGV);
这里的关键是你的代码都不是在模块之外,除了一点点样板来启动你的应用程序运行。这很容易维护,更重要的是,它使编写自动化测试变得容易。无需从命令行运行某些东西,您只需调用具有某些值的函数即可。
无论如何,现在这可能是题外话了。但我认为了解这一点很重要。
简单的答案是不要使用 perl -c 测试编译模块...改用 perl -e'use Module' 或 perl -e0 -MModule。perl -c 旨在对脚本进行测试编译,而不是模块。当您在其中一个上运行它时
递归使用模块时,关键是要确保尽早设置外部引用的任何内容。通常这意味着至少在编译时构造中设置使用 @ISA(在 BEGIN{} 中或通过“使用父级”或已弃用的“使用基础”)并在 BEGIN{} 中设置@EXPORT 和朋友。
基本问题是,如果模块 Foo 使用模块 Bar(它使用 Foo),则 Foo 的编译会在该点停止,直到 Bar 完全编译并且它的主线代码已经执行。确保 Foo Bar 的编译和运行主线代码需要的任何部分都有答案。
(在许多情况下,您可以明智地将功能分离到更多模块中并中断递归。这是最好的。)