习题一:写一支名为&total的子例程,用来返回一串数字的总和。提示:子例程不应使用任何输入/输出,它应该只处理参数并将值返回给调用者。
sub total
{
my $result = 0;
# print "@_\n";
foreach (@_)
{
# print "@_\n";
$result +=$_;
# print "$result \n";
}
$result;
}
以上我的答案,很简单,不是吗?但即便是只有四行有效可执行语句,你能看到里面有三行注释,这反映了我调试的过程,最终定位的问题就是上面的黑体加大部分,没错,就是$_,我开始写的是@_。
可能还是没有完全习惯Perl中不同的前缀的不同含义,$表示Scalar标量,@表示array数组,&表示函数调用等。体现在这个简单的程序中,刚开始接触的人更容易混淆,小骆驼书中也一直强调参数数组@_跟foreach循环中的默认变量@_之间没有任何自动产生的联系。
习题3:写一支名为&above_average的子例程,用来传入一串数字并返回所有大于平均值的数字。
虽然看起来稍微复杂,但有了习题1的经验后,这已经不是什么问题。下面是标准答案
sub total
{
my $result = 0;
foreach (@_)
{
$result +=$_;
}
$result;
}
sub average
{
if(@_ == 0)
{
return;
}
my $sum = &total(@_);
$sum / @_;
}
sub above_average
{
my $average = &average(@_);
my @list=();
foreach $element (@_)
{
if($element > $average)
{
push @list,$element;
}
}
@list;
}
当然这是我最终的解答。但第一次我的答案可不是这样的。我老老实实的按照题目说明,只写了一个子例程&above_average实现了题目的功能,其中包含了求总和、计算平均数、形成大于平均值的数字形成的列表三个功能。其实第一个功能“求和”前面已经实现过了,有现成的例程,但我竟然没有想起重用这个现成的例程。
这可能已经不是技术问题了,是思维模式的问题,“可重用”的思想还没有深入骨髓。所以即便是掌握了子例程编写的技术方法,在思想方面还需要再强化。
复习笔记:
×在Perl中子例程不需要事先声明,直接定义即可,格式为
sub func_and_proceure { }
调用时,推荐使用&,虽然某些情况下可能并不需要这个符号
×返回值可以用return,也可以不用,所以很多Perl子例程返回值采取的是“最后执行的表达式即返回值”的原则。
但如果程序要有多个返回值,如根据不同的条件返回不同的值,则子例程中间的返回值需要用return。
×子例程定义时不需要写参数列表,在使用即被调用时要传入参数,如
$n = &max(1..10);
×子例程实现代码中,@_(Perl中默认的另外一个变量)表示传入参数的列表,在子例程执行期间有效,@_和调用时传入的列表是“复制关系”,即对@_的修改不会影响到被调用时传入的列表。
×@_是子例程的私有变量。子例程可以将参数传给其他的子例程,而不用担心遗失自己的@_变量,如上面的习题3的答案。即使子例程递归调用自己,每次调用时仍然会取得一个新的@_。
×my操作符表示创建的变量是私有的,其作用范围被控制在所属的块中。my不加括号时只能声明一个词法变量,加括号时可以声明多个,如
my $fred, $barney; #错! $barney没有声明
my($fred, $barney); 正确,两个都声明了
×使用use strict编译命令可以让Perl的编译器对当前块或者源文件,强制执行一些确保良好程序设计的规则,就是说,不要太无纪律了,这样容易出错。
如果使用了use strict,Perl会要求你一定要用my来声明每个新的变量。
根据大部分人的建议,比整个屏幕长的程序都应该加上use strict。