在学习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提示符可以非常个性化。
hehe, interesting.