使用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

标签

David Farrell

David是一位职业程序员,他经常在Twitter博客上写代码和编程的艺术。

查看他们的文章

反馈

这篇文章有什么问题吗?请在GitHub上打开一个问题或pull request来帮助我们。