The power of returning functions

在学习Common Lisp的间隙,禁不住手痒,写了两个Perl脚本,第一次尝试将函数作为返回值,感觉相当美妙。其实,返回一个函数并不是什么神奇的法术,在C或C++这样的语言里,我们可以返回一个函数指针,该指针指向程序里的某个函数;然而在Perl里面,作为返回值的函数,不需要是已经存在的函数,我们可以动态地创建一个函数,将其返回。

要理解这里面的不同,首先要理解什么是closure。

sub make_prompt {
    my $n = 0;
    return sub {
        ++$n;
        "shell[$n]>";
    }
}

函数make_prompt返回一个匿名函数,该匿名函数的返回值是个shell提示符,每次执行该函数得到的shell提示符都是不同的,类似于CPAN shell,提示符里含有一个数字,表示提示符出现的次数。

my $prompt = make_prompt();
my $p1 = $prompt->(); # shell[1]>
my $p2 = $prompt->(); # shell[2]>

理解函数make_prompt的关键是其局部变量$n在该函数返回后仍然存在,或者更准确地说,那段空间仍然存在,只是$n不再与其绑定(binding)在一起。换句话说,make_prompt返回的不仅仅是个函数,还包括定义这个函数时与其相关的那部分环境(此例中为$n的空间),使得返回函数可以正确执行。

需要注意的是,每次调用make_prompt时的$n是不同的,所以返回的函数之间没有任何依赖关系

my $prompt1 = make_prompt();
my $prompt2 = make_prompt();
my $p11 = $prompt1->(); # shell[1]>
my $p12 = $prompt1->(); # shell[2]>
my $p21 = $prompt2->(); # shell[1]>

可以看出,closure具有某种程度的封装功能。

理解了closure之后,可以学习currying了。我们稍微修改一下make_prompt的定义:

sub make_prompt {
    my $n = shift;
    return sub {
        my $shell = shift;
        my $m = $n++;
        "$shell[$m]>";
    }
}
my $prompt1 = make_prompt(1);
my $promtp2 = make_prompt(100);
my $p11 = $prompt1->("shell"); # shell[1]>
my $p21 = $prompt2->("root"); # root[100]>

现在,通过传给make_prompt不同的参数,其返回的函数的行为也不一样了,第一个从1开始,第二个从100开始。而且通过让返回函数也接受参数,使得shell提示符可以非常个性化。

One thought on “The power of returning functions

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据