使用Perl匿名函数重用代码
Perl中的匿名函数是一个无名的子程序。但它们有什么好处呢?本文展示了如何通过使用匿名函数来编写更通用、可重用的Perl代码。
想象一下,你已经开发了一个以下脚本的例子。该脚本接收一个目录路径作为参数,并递归地搜索该路径的子目录,打印出它找到的任何文件名
use strict;
use warnings;
use feature qw/say/;
die "Error: you must supply a directory path argument $!" unless @ARGV;
sub listFiles {
my $dir = shift;
opendir(my $DH, $dir) or die "Error: failed to open $dir $!";
while (readdir $DH) {
my $path = $dir . $_;
if(-d $path ){
# recurse but ignore Linux symlinks . and ..
listFiles($path .'/') if $_ !~ /^\.{1,2}$/;
}
elsif(-f $path){
say $path;
}
}
}
listFiles($ARGV[0]);
此脚本最具重用性的方面是其递归目录搜索逻辑。如果你想开发一个文件名搜索脚本(类似于Linux中的find程序),你可以先复制粘贴上面的脚本,然后更新代码以提供所需的行为。另一种方法是修改核心子程序以接受一个匿名函数作为参数,然后在找到每个文件时执行该函数。这样的子程序将类似于以下内容
use strict;
use warnings;
use feature qw/say/;
die "Error: you must supply a directory path argument $!" unless @ARGV;
sub walkDir {
my ($dir, $function) = @_;
# validate args
opendir(my $DH, $dir) or die "Error: failed to open $dir $!";
ref($function) eq 'CODE' or
die "Error: second argument to walkDir must be an anonymous function $!";
while (readdir $DH) {
my $path = $dir . $_;
if(-d $path ){
# recurse but ignore Linux symlinks . and ..
walkDir($path . '/', $function) if $_ !~ /^\.{1,2}$/;
}
elsif(-f $path){
$function->($path);
}
}
}
walkDir($ARGV[0], sub { say shift });
上述代码中的“walkDir”子程序接受两个参数:目录路径和函数。它仍然递归地搜索目录,但是当它遇到文件时,它会取消引用并执行函数,将文件路径作为参数传递给函数。最后一行代码通过调用“walkDir”并传递目标目录路径以及一个打印匿名函数(例如默认参数)来提供文件名打印行为。
我们可以使用相同的walkDir子程序来编写我们的文件搜索脚本,我们只需要更新匿名函数的行为
walkDir($ARGV[0],
sub {
my $filename = shift;
say $filename if $filename =~ /$ARGV[1]/i;
});
要创建一个类似grep的工具,我们可以用以下代码替换之前的代码
use File::Slurp;
walkDir($ARGV[0],
sub {
my $filename = shift;
say $filename if read_file($filename) =~ qr/$ARGV[1]/i;
});
实际上,我们可以快速创建一个有用的系统管理员脚本库,甚至可以将“walkDir”子程序代码放入模块中以提高重用性。希望这些例子展示了通过使用匿名函数,Perl如何让您重用有用的代码。
本文灵感来源于Mark Jason Dominus的《Higher Order Perl》。《Higher Order Perl》探讨了匿名函数和其他函数式编程技术,如递归、柯里化和惰性。它可以在网上免费阅读,并提供电子书格式。
本文最初发布在PerlTricks.com。
标签
反馈
这篇文章有什么问题吗?请在GitHub上打开一个问题或pull request来帮助我们。