Perl学习记录(三))

第四章 子程序

Perl可以让你创建子程序(subroutine) ,也就是用户自定义的函数。它让我们可以重复利用已有的代码。子程序的名称也属于Perl标识符的范畴(即由字母、数字和下划线组成,但不能以数字开头),有时候视情况会以“&” 开头。若无其他声明,我们都将使用该符号,这通常都是比较保险的做法。当然,也有一定不能用&的情形。

定义子程序

要定义你自己的子程序,可使用关键字sub、子程序名(不包含与号)以及用花括号封闭起来的代码块,这部分代码就是子程序的主体。例如:

sub marine {
    $n t= 1;#全局变量$n
    print "Hel1o, sailor number $n|\n";
    ]

子程序可以被定义在程序中的任意位置,你不需要对子程序进行事先声明,子程序
的定义是全局的。假如你定义了两个重名的子程序,那么后面的那个子程序会覆盖掉前面的那个。

调用子程序

你可以在任意表达式中使用子程序名(前面加上与号)来调用它

&marine; # 打印Hello, sailor number 1!
&marine; #打印He11o, sailor number 2!
&marine;#打印Helo, sailor number 3!
&marine; # 打印Hello, sailor number 4!

通常,我们把对子程序的调用称为呼叫(calling) 子程序。

返回值

子程序被调用时一定是作为表达式的某个部分,即使该表达式的求值结果不会被用到。.之前我们在调用&marine时,先对包含调用动作的表达式求值,但随即就把结果丟弃了。
很多时候,我们需要调用某个子程序并对它的返回值作进一步的处理。所以我们需要注意子程序的返回值。在Perl中,所有的子程序都有一个返回值,子程序并没有“有返回值”或“没有返回值”之分。但并不是所有的Perl子程序都包含有用的返回值。既然任何Perl子程序都有返回值,那么规定每次必须写“return" 某值就显得非常费事。在子程序的执行过程中,它会不断进行运算,而最后一次运算的结果(不管是什么)都会被自动当成子程序的返回值。
比如我们定义下面这个子程序,最后一个是加法表达式:

sub sum_of_fred_and_barney
print "Hey, you called the sum_of_fred_and_barney subroutine! \n";
$fred + $barney; # 这就是返回值
}

这个子程序里最后执行的表达式就是计算 f r e d 与 fred与 fredbarney的总和。因此, f r e d 与 fred与 fredbarney的总和就是返回值。以下是实际运行的情况:

$fred=3;
$barney=4;
$wilma = &sum_of_fred_and_barney;# $wilma 为7
print "\$wilma is $wilma. \n";
$betty = 3 * &sum_of_fred_and_barney; # $betty为21
print "\$betty is $Sbetty. \n";

这段代码会输出以下内容:
Hey, you called the sum_of _fred_and_barney subroutine!
$wilma is 7.
Hey, you called the sum_of fred_ and_ barney subroutine!
$betty is 21.

此处的print语句只用于协助调试,让我们得以确定该子程序被调用到了,程序完工后便可将它删除。不过,假设你在这段程序代码的结尾新增一条print语句, 像这样:

sub sum_of_fred_and_barney {
print "Hey, you called the sum_of_fred_and_barney subroutine!\n" ;
$fred + $barney; # 这不是返回值!
print "Hey, I'm returning a value now! \n" ;#糟糕!
}

最后执行的表达式并非加法运算,而是print语句。它的返回值通常是1,表示“成功输出信息”,但它不是我们真正想要返回的值。所以在子程序里增加额外的程序代码时,请小心检查最后执行的表达式是哪一个,确定它是你要的返回值。
那么,那个第二个(不完善的)子程序中 f r e d 与 fred与 fredbarney相加的结果我们并没有将总和存储起来,所以Perl会丢弃它。
“最后执行的表达式”的准确含义真的就是最后执行的表达式,而非程序代码的最后一行。比如下面这个子程序,它会返回 f r e d 和 fred和 fredbarney两者中值较大者:

sub larger_of_fred_or_barney {
if ($fred > $barney) {
$fred;
} else {
$barney;
}
```i
最后执行的表达式是自成一行的$fred或$barney, 所以它们将充当子程序的返回值。必须等到执行阶段得知这些变量的内容后,我们才会知道返回值到底是$fred还是$barney。


## 参数
Perl子程序可以有参数(argument) 。要传递参数列表到子程序里,只要在子程序调用的后面加上被括号圈引的列表表达式就行了。例如:
```perl
$n = &max(10, 15); # 包含两个参数的子程序调用

参数列表将会被传入子程序,让子程序随意使用。当然,得先将这个列表存在某处,Perl会自动将参数列表化名为特殊的数组变量@,该变量在子程序执行期间有效。子程序可以访问这个数组,以判断参数的个数以及参数的值。
这表示子程序的第一个参数存储于 [ 0 ] , 第 二 个 参 数 存 储 于 _[0], 第二个参数存储于 [0]
[1], 依此类推。但是,请特别注意,这些变量和 变 量 毫 无 关 联 , 就 像 _变量毫无关联,就像 dino[3] (数组@dino中的元素之一)与KaTeX parse error: Expected 'EOF', got '&' at position 81: …已。 现在,你可以写一个类似于&̲larger_of_fred_…_ [0])而不用 f r e d , 也 可 以 使 用 子 程 序 的 第 二 个 参 数 ( fred,也可以使用子程序的第二个参数( fred,使(_ [1])而不用$barney。因此,最后你可以写成这样:

sub max {#请比较它和子程序&larger_of_fred_or_barney的差异
if ($_[0] > $_[1]) {
$_[0];
} else {
$_[1];
}

这里的一堆下标让程序变得不雅观,而且难以阅读、编写、检查和调试。不用担心,我们马上就会看到更好的办法。这个子程序还存在另一个问题。&max这个名字虽然既好听又简洁,但却没有说明这个子程序只接受两个参数:

$n = &max(10, 15, 27); #糟糕!

多余的参数会被忽略,反正子程序也不会用到$_[2],所以Perl并不在乎里面是否有值。参数如果不足也会被忽略,如果用到超出@_数组边界的参数,只会得到undef。
实际上,@变量是子程序的私有变量
假如已经有了全局变量@
,则该变量在子程序调用前会先被存起来,并在子程序返回时恢复原本的值。这也表示子程序可以将参数传给其他程序,而不用担心遗失自己的@_变量。就算是嵌套的子程序(nested subroutine)调用它自己的@_变量时也一样。即使子程序递归调用自己,每次调用的仍然是一个新的@。所以在当前的子程序调用中,@ _总是包含了它的参数列表。

子程序中的私有变量

既然每次调用子程序时Per1都会给我们新的@_,难道不能利用它构造私有变量吗?答案当然是可以。
默认情况下,Perl里面所有的变量都是全局变量,也就是说,在程序里的任何地方都可以访问它们。但你随时可以借助my操作符来创建私有变量,我们称之为词法变量(lexical variable) :

sub max {
   my($m$n);#该语句块中的新私有变量
   ($m, $n)=@_;#将参数赋值给变量
   if($m>$n){$m}else{$n}
}

这些变量属于封闭语句块的私有变量,语句块之外任意地方的 m 或 m或 mn都完全不受这两个私有变量的影响。反过来也是,外部变量同样无法影响内部的私有变量。所以,我们
可以把这个子程序放进世界上任何一个Perl程序里,不用担心它和哪个程序中(可能存
在)的 m 和 m和 mn变量冲突。另外值得一提的是,在前一个例子中的if语句块中,作为返回值的表达式后面没有分号。虽然Perl允许你省略语句块中最后一个分号。但实际上通常只有像前面的例子那样,代码简单到整个语句块内只有一行时,才可以省略分号。
前一个例子中的子程序还可以进一步简化。你是否注意到列表($m, $n)出现了两次?
其实my操作符也可以应用到括号内的变量列表,所以习惯上会将这个子程序中的前两行语句合并起来:

 my($m, $n) = @_; #对子程序的参数命名

这一行语句会创建私有变量并为它们赋值,让第一个参数的名称变成较好记的 m , 而 第 二 个 参 数 的 则 为 m,而第二个参数的则为 mn。几乎所有的子程序都会以类似的程序代码作为开头。当你看到这一行时就会知道,这个子程序具有两个标量参数,而在子程序内部,它们分别称为 m 和 m和 mn。

变长参数列表

把更长的(任意长度的)列表作为参数传给子程序。
通过检查@数组的长度其实也很容易确定参数个数是否正确。比方说,我们可以将&max写成下面这样以检查其参数列表

sub max {
if(@_!=2){
print "WARNING! &max should get exactly two arguments!\n" ;
#其余代码和前面一样......
}

上面的if判断是在标量上下文中直接使用数组“名称”来取得数组元素的个数。但在实际编写的PerI程序中这种检查方式很少见,更好的做法是让子程序自动适应任意数目的参数。

改进的&max子程序

$smaxirum=amnax(3, 5, 10, 4; 6);
sub max {
    my($max_so_far) = shift @_ ; # 数组中的第一个值, 暂时把它当成最大值
    foreach (@_) { #遍历数组@_中的其他元素
         if ($_> smax_so_far) { #当前元素比$max. so_ fa更大吗?
             $max_so_far . $_ ;
             }
         $max_ so_ far;
         }

上面的程序代码使用了一般称为“ 高水线(high-watermark) ”的算法:大水过后,在最后一波浪消退时,高水线会标示出所见过的最高水位。本例中, m a x s o f a r 记 录 了 高 水 线 , 所 以 最 后 max_so_far记录了高水线,所以最后 maxsofar线max_so_far变量中的值就是我们要找的最大值。
第一行程序代码会对参数数组@进行shift操作并将所得到的3 (范例程序里的第一个参数)存入 m a x s o f a r 变 量 。 所 以 @ 现 在 的 内 容 为 ( 5 , 10 , 4 , 6 ) , 因 为 3 已 被 移 走 。 现 在 最 大 的 数 字 是 目 前 唯 一 见 过 的 值 : 3 , 也 就 是 第 一 个 参 数 。 然 后 f o r e a c h 循 环 会 遍 历 参 数 列 表 @ 里 剩 余 的 元 素 。 循 环 的 控 制 变 量 默 认 为 max_so_far变量。所以@现在的内容为(5,10,4,6),因为3已被移走。现在最大的数字是目前唯一见过的值: 3, 也就是第一个参数。然后foreach循环会遍历参数列表@_里剩余的元素。循环的控制变量默认为 maxsofar@(5,10,4,6)3:3,foreach@。循环第一次执行时, 是 5 , 而 i f 进 行 比 较 时 看 到 _是5,而if进行比较时看到 5if_比 m a x s o f a r 还 大 , 所 以 max_so_far还大,所以 maxsofarmax_so_far会被设成5(新的高水线)。第三次循环时, 是 10 。 这 是 新 的 最 大 值 , 所 以 它 会 被 存 入 _ 是10。这是新的最大值,所以它会被存入 10max_so_far。到第四次时, 是 4 。 这 时 i f 比 较 的 结 果 为 假 , 因 为 _ 是4。这时if比较的结果为假,因为 4if_不比 m a x s o f a r ( 即 10 ) 大 , 所 以 会 跳 过 i f 里 的 程 序 代 码 。 最 后 , max_ so_ far (即10)大,所 以会跳过if里的程序代码。最后, maxsofar(10)if_是6,因此if里的程序代码又被跳过一次。这是最后一次执行循环, 所以整段循环就执行完了。
此时,$max_so_far就变成了我们的返回值。既然它是目前见过的最大值,并且我们已经
遍历过所有数字,那它一定是列表中最大的值:10

空参数列表
没有任何參数传入,又会发生什么事情呢?比如

$maximum = &max(@numbers);

数组@numbers有时可能只是一个空列表。也许数组内容是程序从文件读入的,但文件却是空的。
子程序的第一行会对参数数组@_ (现在是空的) 进行shift操作,以此作为 m a x s o f a r 的 值 。 这 并 不 会 出 错 , 因 为 数 组 是 空 的 , 所 以 s h i f t 会 返 回 u n d e f 给 max_so_far的值。这并不会出错,因为数组是空的,所以shift会返回undef给 maxsofarshiftundefmax_so_far,现在foreach循环要遍历@_数组,但由于它是空的,所以循环本身不会执行。
接下来会将$max_so_far的值undef作为子程序的返回值。从某种角度来看,那是
正确的结果,因为在空列表中没有最大值。

关于语法(my)变量

词法变量可使用在任何语句块内,而不仅限于子程序的语句块。比如说,它可以在if、while或 foreach的语句块里使用:

foreach (1..10) {
my($square) = $_ * $_; #该循环中的私有变量
print "$_ squared is $square.\n";
}

上面的例子中,变量 s q u a r e 对 其 所 属 语 句 块 ( 也 就 是 f o r e a c h 循 环 的 语 句 块 ) 来 说 是 私 有 的 。 如 果 变 量 的 定 义 并 未 出 现 在 任 何 语 句 块 里 , 则 该 变 量 对 于 整 个 程 序 源 文 件 来 说 就 是 私 有 的 。 词 法 变 量 的 作 用 域 ( s c o p e ) 受 限 于 定 义 它 的 最 内 层 语 句 块 ( 或 文 件 ) 。 只 有 语 句 块 上 下 文 作 用 域 内 的 程 序 代 码 才 能 以 square对其所属语句块(也就是foreach循环的语句块)来说是私有的。如果变量的定义并未出现在任何语句块里,则该变量对于整个程序源文件来说就是私有的。词法变量的作用域(scope)受限于定义它的最内层语句块(或文件)。只有语句块上下文作用域内的程序代码才能以 square(foreach)(scope)()square这个名称使用该变量。这为程序维护提供了便利。如果$square的值出错了,那么就可以在有限的源代码范围内找到原因。
还需要注意的是,my操作符并不会更改变量赋值时的上下文:

my($num) = @_; #列表上下文,和($num) = @_;相同
my $num = @_; #标量上下文,和$num = @_;相同

在第一行里,按照列表上下文,$num会被设为第一个参数;在第二行里,按照标量上下文,它会被设为参数的个数。看到这样的程序代码时,你可以忽略my这个词,直接判断变量
赋值时的上下文。
请记住,在my操作符不加括号时,只能用来声明单个词法变量:

my $fred, $barney; #错!没声明$barney
my($fred, $barney); #两个都声明了
#当然,你也可以使用my来创建新的私有数组
my @phone_number;

所有新变量的值一开始都是空的:标量被设为undef,数组被设为空列表。
你最好对每个新变量都使用my声明,让它保持在自己所在的词法作用域内。在第三章中,你已经看到过如何在foreach循环中定义自己的控制变量,而这个控制变量也可以声明为词法变量:

foreach my $rock (qw/bedrock slate lava /) {
print“One rock is $rock.\n"; # 依次输出每块石头的名字
}

use strict编译指令

希望Perl能更严格,多一点约束力,可以use strict编译指令 。
所谓编译指令(pragma) ,其实不过是提供给编译器的某些指示,告诉它如何处理接下来的代码。这里的use strict编译指令是要告诉Perl内部的编译器接下来( 代码块或是程序源文件中)的代码应该稍加严谨点,遵循优良的编程风格。
比如:

$bamm_bamm = 3; #Perl会自动创建这个变量
#然后你继续写了一些代码。当上一行代码被新写的代码挤出屏幕顶端后,你又写了下面这行代码,想增加那个变量的值:
$bammbamm += 1; #糟了!

因为Perl看到了一个新的变量名(变量名中的下划线是有意义的),它会创建一个新的变量,然后增加它的值。如果你有先见之明,已经启用了警告功能,那么Perl就会警告你这两个(或其中之一)全局变量名在程序中只出现过一次。可是,如果你不够谨慎,那么这两个变量名都会在程序里并存,Perl也并不发出警告。
要告诉PerI你愿意接受更严格的限制,请将use strict这 个编译指令放在程序开头(或者任何需要强制使用约束规则的语句块或文件内):

use strict; #强制使用一些严格的、良好的编程风格
use 5.012; #自动加载strict编译指令

现在,所有的约束当中,Perl首先会坚持要求你声明所有新出现的变量。一般加
上my就可以了

my $bamm_bamm = 3; # 新的词法变量
$bammbamm += 1; #无此变量:属于编译时致命错误

现在,拼错变量名,报错说从未声明过 b a m m b a m m 这 个 变 量 , 因 此 错 误 在 编 译 阶 段 就 会 被 找 出 来 : 当 然 , 此 限 制 只 适 用 于 新 创 建 的 变 量 , P e r I 的 内 置 变 量 ( 比 如 bammbamm这个变量,因此错误在编译阶段就会被找出来: 当然,此限制只适用于新创建的变量,PerI的内置变量(比如 bammbamm:PerI(和@)则完全不用事先声明。
tips:比整个屏幕长的程序都应该加上use strict,最好在开始写程序时就用它。

return操作符

如果想在子程序执行到一半时停止执行,使用return操作符,它会立即停止执行并从子程序内返回某个值:

my @names = qw/ fred barney betty dino wilma pebbles bamm-bamm /;
my $result = &which_element_is(" dino", @names);

sub which_element_is {
    my($what, @array) = @_;
    foreach (0. .$#array) { # 数组@array中 所有元素的索引
         if ($what eq $array[$_ ]) {
             return $_ ; #一发现就提前返回
             }
     }
  -1;
 }    #没找到符合条件的元素(写不写return都没关系)

我们希望这个子程序能找出@names数组中值为dino的元素的索引。首先,用my声明参数名:一个是 w h a t 表 示 要 搜 索 的 内 容 , 另 一 个 是 @ a r r a y , 表 示 供 搜 索 的 目 标 数 组 。 这 个 数 组 其 实 是 @ n a m e s 的 拷 贝 。 f o r e a c h 循 环 依 次 取 出 @ a r r a y 的 索 引 值 ( 第 一 个 索 引 值 为 0 , 最 后 一 个 索 引 值 为 what表示要搜索的内容,另一个是@array,表示供搜索的目标数组。这个数组其实是@names的拷贝。foreach循环依次取出@array的索引值(第一个索引值为0,最后一个索引值为 what@array@namesforeach@array(0#array,这个我们已经在第三章见到过了)。
每一次执行foreach循环时,我们会检查$what中的字符串是否等于位于当前索引的数组@array的元素。如果两者相等,我们就立刻返回其索引值。这是关键字return最常见的用法:立即返回某个值,而不再执行子程序的其余部分。
但是,假如我们找不到符合条件的那个元素呢?本例中,子程序的作者选择返回-1作为“查无此值”的数字代号。虽然在这个例子里,返回undef可能会更符合Perl的风格,但他还是决定用-1。最后一行写成return -1也行, 但实际上return并不是必需的。
有些程序员在每次返回值时都会用return,以明确表明它是返回值。比如在子程序执行
到中间部分就已经得出结论的话,就可以使用return关键字立即返回,就像本章之前提
到&larger_of_fred_or_barney时说的那样。而到子程序末尾再返回结果的话,则完全可
以省略return。
省略与号
在调用子程序时何时可以省略与号。如果编译器在调用子程序前看到过子程序的定义,或者Perl通过语法规则判断它只能是子程序调用,那么对待该子程序就可以像内置函数那样,在调用时省略与号。
换句话说,假如Perl单从语法上便能看出它是省略了与号的子程序调用,也就是说,你只要将参数列表放进括号里,它就一定是函数调用:

my @cards = shuffle(@deck_of_cards); # &shuffle 上的&是多余的

或者,如果Per1的内部编译器已经见过子程序的定义,那么通常也可以省略与号。并
且,连参数列表两边的括号都可以省略:

sub division {
    $_[0] / $_[1];#用第一个参数除以第个参数
}
my $quotient = division 355, 113; # 用的就是之前定义的&division

上面的程序之所以能够运行,是因为它符合“加不加括号都不会产生歧义”的原则。
但不要把子程序定义放在调用语句的后面,否则编译器就无法提前判断division的意义。编译器需要在子程序调用前先看到子程序定义,才有办法像对内置函数般调用该子程序;否则,编译器不知道该如何处理这个表达式。
例外是:假如这个子程序和PerI内置函数同名, 你就必须使用与号调用。原因很简单,也是为了避免歧义。加上与号,就说明调用的是你自己定义的子程序。所以只能在没有同名内置函数的情况下省略与号:

sub chomp {
print "Munch, munch!\n";
]
&chomp; #这里必须使用&,绝不能省略!

真正的省略规则如下:
除非你知道Perl所有的内置函数名,否则请务必在调用函数时使用与号。这意味着,你最初写的100个程序应该始终加上与号。但若看到别人的程序中省略与号的写法,那未必是错的,也许他很清楚Perl没有这个名字的内置函数

非标量返回值

子程序不仅可以返回标量值,如果你在列表上下文调用它,它还能返回列表值。
假设你想取出某段范围的数字(如同范围操作符…的递增序列),只是你不但想递增,
还想递减序列。虽然范围操作符只能产生递增序列,不过要反过来取也不是什么难事:

sub list_from_fred_to_barney {
    if ($fred < $barney) {
       #从$fred数到$barney
        $fred. . $barney;
    } else {
      #从$fred倒数回$barney
      reverse $barney..$fred;
      }
    }
$fred = 11;
$barney = 6;
@c = &list_from_fred_to_barney; # @c的值为(11, 10, 9, 8, 7, 6)

我们会先用范围操作符取得从6到11的列表,再用reverse操作符把它反转。最后的结果就是我们想要的:从 f r e d ( 11 ) 倒 数 到 fred(11)倒数到 fred(11)barney(6)的列表。
其实你还可以什么都不返回。单写一个return不给任何参数时,在标量上下文中的返回值就是undef,在列表上下文中则是空列表。这通常用于表示子程序执行有误,它告诉调用者无法取得有意义的返回值。

持久化私有变量

在子程序中可以使用my操作符来创建私有变量,但每次调用这个子程序的时候,这个私有变量都会被重新定义。而使用state操作符来声明变量,我们便可以在子程序的多次调用期间保留变量之前的值,并将变量的作用域局限于子程序内部。
回到本章第一个例子,我们有个名为marine的子程序,它会使全局变量的值每次递增:

sub marine {
$n +=1; # 全局变量$n
print "Hello, sailor number $n!\n";

我们可以用state来声明变量,它会告诉Perl该变量属于当前子程序的私有变量,并且在
多次调用这个子程序期间保留该变量的值。

use 5.010;
sub marine {
state $n=0;#持久性私有变量$n
$n += 1;
print "He1lo, sailor number $n!\n";

现在我们可以在用了strict编译指令并且不用全局变量的前提下得到和之前相同的输
出。第一次调用该子程序时,Perl声明并初始化变量 n , 而 在 接 下 来 的 调 用 中 , 这 个 表 达 式 将 被 P e r l 忽 略 。 每 次 子 程 序 返 回 后 , P e r I 都 会 将 变 量 n,而在接下来的调用中,这个表 达式将被Perl忽略。每次子程序返回后,PerI都会将变量 nPerlPerIn的当前值保留下来,以备下次
调用时再用。

类似标量变量,其他任意类型的变量都可以被声明为state变量。下面的子程序可以通过声明state数组来保留它的参数及计算总和:

use 5.010;
running_sum( 5, 6 );
running_sum( 1..3 );
running_sum( 4 );
sub running_sum {
  state $sum = 0;
  state @numbers;
  foreach my $number ( @_ ) {
    push @numbers$number;()
    $sum = $number;
  }
  say "The sum of (@numbers) is $sum" ;
  }

每次我们调用这个子程序的时候,它都会将新的参数与之前的参数相加,因此每次都会
输出一个新的总和:
Thesumof(56)is11
Thesumof(56123)is17
The sum of (561234) is 21

但是在使用数组和哈希类型的state变量时,不能在列表上下文中初始化这两种类型的state变量:

state @array = qw(a b c); #错误!

习题

1.[12]写一个名为total的子程序,它可以返回给定列表中数字相加的总和。提示:该子程序不需要执行任何I/O,它只需要按要求处理它的参数并给调用者返回一个值就行了。用下面这个程序来检验一下你写完的子程序,第一次调用时返回的列表中数字的总和应该是25。

my @fred=qw{1 3 5 7 9};
my $fred_total = total(@fred);
print "The total of \@fred is $fred_total. \n";
print "Enter some numbers on separate lines: ";
my $user_total = total(<STDIN>);
print "The total of those numbers is $user_total. \n";

1

  1. [5]使用之前程序中的子程序,计算从1加到I000的总和。
    2参考答案:更简单的方式——直接在print里调用子程序(注意不可在“ ”中调用)
print "the numbers from 1 to 1000 add up tp",total(1..1000),"\n";
  1. [18]额外附加题: 写一个名为&above_average的子程序,当给定一个包含多个数字的列表时,返回其中大于这些数平均值的数。(提示: 另外写一个子程序,通过用这些数的总和除以列表中数字的个数来计算它们的平均值。)当你写完后,用下面的程序检验一下。
my @fred = above_average(1..10);
print "\@fred is @fred\n";
print "(Should be 6 7 8 9 10)\n";my @barney = above_average(100, 1..10);
print "\@barney is @barney\n" ;
print "(Should be just 100)\n";

3
参考答案:

sub average{
if (@_==0){return}
my $conut=@_;
my $sum=total(@_);
$sum/$count;
}

sub above_average{
my $average =average(@_)
my @list;
foreach my $element(@_)
    if ($element>$average){
    push @list,$element;}
}
@list;
}
  1. [10]写一个名为greet的子程序, 当给定一个人名作为参数时,打印出欢迎他的信息,并告诉他前一个来宾的名字:
    greet( “Fred” );
    greet( “Barney” )
    按照语句的顺序,它应该打印出:
    Hi Fred! You are the first one here!
    Hi Barney! Fred is also here!
    4
use 5.010;
greet('Fred';
greet('Barney');
sub greet{
    state $last_person;
    my $name=shift;
    print "HI $name";
    if (defined $last_person){
        print "$last_person is also here!\n";}
    else{
        print "you are the first one here!\n"}
    $last_person=$name;
}
  1. [10]修改 前面这个程序,告诉所有新来的人之前已经迎接了哪些人:
    greet( “Fred” );
    greet( “Barney” );
    greet( “Wilma”
    greet( “Betty” );.
    按照语句的顺序,它应该打印出:
    Hi Fred! You are the first one here!
    Hi Barney! I’ve seen: Fred
    Hi Wilma! I’ve seen: Fred Barney
    Hi Betty! I’ve seen: Fred Barney Wilma
    5
use 5.010;
greet('Fred';
greet('Barney');
greet('Wilma';
greet('Betty');
sub greet{
    state $name;
    my $name=shift;
    print "HI $name";
    if (@names){
        print "$last_person is also here!\n";}
    else{
        print "you are the first one here!\n"}
    push @names,$name;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值