1.Whatdoesthisdo?
$foo=$foo[1]
将数组@foo的第2个元素赋给标量$foo
2.Whatdoesthisprint?
@a=();
$h{'a'}='b';
push@a,%h;
print"@a";
push的参数除了第1个以外都处于列表环境当中,而散列在列表环境中会将其中每个键值对拆开,组合成一个大列表。因此
push@a,%h;
就相当于
push@a,'a','b';
最后经过内插以后的输出结果就是
ab
需要注意的是散列转化为列表时,每个键值对的位置是不固定的,这里只有一个键值对看不出来。如果散列变成
%h=('a'=>'b','c'=>'d');
那么“@a=%h”的结果可能是以下两种中的某一个
@a=('a','b','c','d');
@a=('c','d','a','b');
3.Inthiscode
@x=('a','b','c');
$y=scalar('a','b','c');
$z=scalar@x;
whatare$yand$zsetto?
“('a','b','c')”是列表,“@x”是数组,二者在标量环境下求值的结果是不同的:列表在标量环境下求值得到的是最后一个元素,数组在标量环境下求值则得到数组本身的长度。因此上面$y赋值得到'c',而$z赋值得到3。在赋值操作中,赋值号右边内容所处的环境完全由赋值号左边的内容决定,如果赋值号左边是数组、散列或列表,则右边就处于列表环境中,若左边是标量则右边也处于标量环境中。这里两个赋值操作的左边都是标量,因此右边的scalar操作实际上没有必要,去掉该操作以后结果不会改变。
4.Whatdoesthisdo?
$c='foo';
$a="$c=\'\\\'\n";
$b='$c=\'\\\'\n';
双引号中的所有转义序列都有效,且变量会进行内插;单引号中的有效转义序列仅有“\'”和“\\”,不进行内插操作。如此结论就很明显了。
5.Whatdoesthisdo?
while(<STDIN>){
chomp;
unless($_){
print"emptyline\n";
}
}
Perl中在布尔环境下求值为假的量只有未定义值undef、空字符串''、数值0和字符串'0'这几个,其余的量在布尔环境中都是真值,因此这里的unless语句不只挑出了所有空行,还把只含有一个字符0的行也挑出来了,达不到原来的目的。
6.Characterizethepossibleoutcomesofthiscode
@s=sortfoo(1,4,3,2);
assumingthatfooisauser-definedsubroutine.
题目原来想表现的问题是自定义比较函数返回结果不一致的时候(比如头一次结果1大于2,下一次结果变成1小于2,这就是比较结果不一致),sort可能会出错乃至崩溃。所幸的是这个问题只在Perl5.004之前的版本有,后来版本的sort对这个情况已经不敏感了(不敏感的意思就是在这种情况下排序的结果不一定对,但是肯定不会出错)。其实函数foo的定义方式也值得说明一下,当自定义比较函数没有函数原型时,sort向比较函数传递参数是通过和函数处于同一名字空间的全局变量$a和$b进行的;而当函数具有$$这样的原型时,sort通过比较函数的参数数组@_传递参数,需要注意的是原型只要不是$$,就会按照没有函数原型的情况传递参数。用有原型的比较函数排序一般会比没有原型的时候慢一些。此外,解释器对sort的语法解析太要命:
sortfunc(1,2,3,4);#对列表1,2,3,4使用比较函数func进行排序
sortfunc1,2,3,4;#和上面一样,很正常
sortfunc(1,2,3,4);#和上面一样!!
sort(func1,2,3,4);#和上面一样!!
sort(func(1,2,3,4));#和上面一样!!
sort(func(1,2,3,4));#对func(1,2,3,4)返回的列表进行默认排序!!
sort((func(1,2,3,4)));#和上面一样,很正常
sort((func(1,2,3,4)));#语法错误!!
func有没有原型似乎不会影响对sort参数的解释,也就是说就算你事先定义过subfunc($$$$)或者subfunc(@),也绝对不会改变上面列出来的结果。因此为了尽量少给自己找麻烦,大家最好还是用尽简单的sort形式,千万别在没把握的情况下随意把多行代码合并到sort参数里去。
7.Whatdoesthisdo?
$[=42;
唔,没什么好说的,查perldocperlvar
8.Whatdoesthisdo?
$x=(sort=>(4,8,6));
$y=sort=>(4,8,6);
“=>”和逗号等价,只是编译时会把它左边的参数当作字符串处理。换个马甲改变不了本质,它的优先级和逗号一样低。因此题目中的代码相当于以下形式:
$x=("sort",4,8,6);
($y="sort"),4,8,6;
前一个语句把列表赋给标量,标量得到的是列表的最后一个元素,因此$x=6;后一个语句把字符串赋给标量,然后赋值结果也就是字符串“sort”和剩余的值组成一个列表,在空环境中被丢弃,最后$y="sort"。
9.Whatdoesthisdo?
print(1+2)+3;
print+(1+2)+3;
经典问题,在Perl中看起来“像”函数调用的语句就被处理成函数调用,给上面两行加上括号:
(print(1+2))+3;
print(+(1+2)+3);
结果就很清楚了,最后输出“36”。
10.Whatdoesthisdo?
$mt=stat($file)[8];
assumingthat$fileisavalidfilehandle?
$file再有效也改变不了语法错误的命运,列表环境的括号不能省,应该改成(stat($file))[8]。
11.WhatwillthisPerlregularexpressionmatch?
/$foo[bar]/
Characterizeallofthepossibilities.
这是典型的变量内插产生二义性的例子,题目中的表达式可以被解释为${foo}[bar]或者${foo[bar]},即解释为标量$foo的内插或者数组元素$foo[bar]的内插,但明显后一种在这里解释不通,因为字符串“bar”并不能作为数组的下标,所以Perl只有使用前一种解释。但当“[]”中的字符串看起来像常数、变量或者内置关键字的时候就是另一回事了,如:
/$foo[length]/解释为/${foo[length($_)]}/
/$foo[01]/解释为/${foo[1]}/
/$foo[$x]/解释为/${foo[$x]}/
而且即使被解释为下标的变量不存在,正则表达式引擎仍然会尝试访问,这样在打开strict的时候会产生变量未声明的错误。为了避免不必要的歧义,使用正则表达式的时候最好不要把内插变量放在字符集合之前。
12.Giventhissubroutine
subsomefunc{
keys%somehash,0;
}
andassumingthat%somehashisdefined,whatisthevalueofsomefunc()?
Perl里的函数返回值所处的环境是和调用函数时的环境一样的,因此如果调用形式是
@array=somefunc;
那么“keys%somehash,0”就处于列表环境之中,会把%somehash的所有键和0组成一个列表返回。然而调用形式如果是
$var=somefunc;
它们所处的环境就相应地变成标量环境,而将生成的列表强制转变为标量,也就是返回列表的最后一个值“0”。
13.Whatdoesthisdo?
$SIG{PIPE}=handler;
别忘了函数引用是\&func而不是直接用func,题目中的那个语句是先调用函数handler,再将其返回值赋给$SIG{PIPE},这和预期的修改自定义信号处理函数这一功能可是差了十万八千里(当然,如果你说handler本来就要返回一个函数引用,这个语句大概就没什么问题了)。
14.Howmightthisexpressionbeevaluated?
x+2
Enumerateallofthepossibilities.
该表达式如何求值主要看x是否定义为函数以及函数是否存在原型。求值的结果可能是:
"x"+2没有任何关于x的定义,因此将其视为裸字,转换为字符串(前提是没有打开strict)参与计算,最终得到数值2。
x(+2)x定义为没有原型的函数,产生函数调用。
x(+2)x定义为带有$、@、%或者其他接受列表参数原型的函数,产生函数调用。
x()+2x定义为带有空原型的函数,产生无参数函数调用并将结果同2进行计算。
x定义为具有其他原型的函数时,将由于参数和原型不匹配而产生语法错误。
15.Whatdoesthisdo?
@foo[1]=<STDIN>;
只要变量前面的符号是@,那就意味着产生了一个数组,即使下标只有一个也一样。给数组赋值时会形成列表环境,而文件读取操作符<>在列表环境中的行为是按行读入所有文件内容,每行形成一个列表元素。这一列表赋给数组切片@foo[1]时,只有列表的第一个元素赋给了$foo[1],列表的其他内容都被丢弃。因此整个语句的作用就是读入文件所有内容,将第一行赋给数组@foo的第二个元素。
16.Whatdoesthisdo?
$i=0;
do{
$done=foo();
if($done){last;}
$i++;
print"x";
}while($i<10);
“do{}”只是一条语句,而不是块,因此不能在其中使用循环控制语句redo、next、last等,但只有在确实执行到这些语句时出问题。比如题目中的程序在$done为真的情况下才会出错。要想在“do{}”结构中使用循环控制,必须用大括号人为制造块,比如如果想使用last控制语句,就需要写成:
{
do{
...
last;
}while(...);
}
而要使用next时,要写成:
do{{
...
next;
}}while(...);
这些都是由循环控制语句的特性决定的,可以根据具体情况和需求自行创造符合要求的格式。
17.Explainwheneachof``exists''or``defined''willbeprinted.
print"exists"ifexists$foo{$bar};
print"defined"ifdefined$foo{$bar};
exists在给定参数的存储空间已分配时返回真,它不在意这块空间中究竟存放了什么值;defined只有在给定参数求值不为undef时才返回真,对未分配空间defined返回假,因为对它求值时得到的将是undef。因此要打印“exists”,必须保证$foo{$bar}已经被赋过值;要打印“defined”,必须保证$foo{$bar}被赋过非undef的值。还要注意一点就是,exists只能对数组或散列的元素使用,而任意表达式都可以作为defined的参数。下面一个例子简单地说明了一下二者的区别:
@a=();
print"1.\$a[0]exists\n"ifexists($a[0]);#nothing
print"2.\$a[0]defined\n"ifdefined($a[0]);#nothing
$a[0]=undef;
print"3.\$a[0]exists\n"ifexists($a[0]);#something
print"4.\$a[0]defined\n"ifdefined($a[0]);#nothing
$a[0]=0;
print"5.\$a[0]exists\n"ifexists($a[0]);#something
print"6.\$a[0]defined\n"ifdefined($a[0]);#something
undef($a[0]);
print"7.\$a[0]exists\n"ifexists($a[0]);#something
print"8.\$a[0]defined\n"ifdefined($a[0]);#nothing
delete($a[0]);
print"9.\$a[0]exists\n"ifexists($a[0]);#nothing
print"10.\$a[0]defined\n"ifdefined($a[0]);#nothing
18.Whatdoesthisdo?
for($i=0;$i<10;$i++){
if(<STDIN>){print;}
}
if并没有while那样隐含对$_赋值的功能,因此这里的if语句从标准输入读入一行后print不会输出任何内容。
19.Whatdoesthisdo?
$f=<foo[bar]>;
这是一个fileglob,由于是在标量环境下赋值,只会返回首先找到的那个文件名,而字符集合“[bar]”在匹配时是按顺序调整成“[abr]”进行的,因此查找顺序是“fooa”、“foob”、“foor”,如果这些文件都不存在则返回未定义值undef。
20.Whatdoesthisprint?
$_="foobarbaz";
--$b{(/(\w+)/)[0]};
--$b{/(\w+)/};
print%b;
“m//”操作符简写为“//”,在没有用“=~”或“!~”将其绑定到字符串上时默认对“$_”进行操作。没有加“g”修饰符时,在列表环境中它返回捕获内容组成的列表,标量环境中它返回匹配成功与否(分别用1和空字符串表示)。题目里第一个自减语句中的正则表达式处于“(...)[0]”构造的列表环境当中,因此返回捕获内容列表;第二个自减语句处于标量环境中,返回表示匹配成功的1。故最终结果相当于:
--$b{foo};
--$b{1};
print%b;
最后输出是“foo-11-1”或“1-1foo-1”。
21.Whatdoesthisprint?
print-0.5**-0.5;
Perl中幂运算“**”的优先级高于单目取反运算符“-”,因此实际解释为:
print-(0.5**(-0.5));#-1.4142135623731
22.Whatdoesthisprint?
@a=(0=>0);
print~$a[0],"",~$a[1];
由于使用了“=>”操作符,按照perldocperlop文档中的定义,@a的第一个元素本来应该解释为字符串“0”。然而在5.8.x的Perl中,后面对该元素的取反操作仍然是按照数值方式进行的,说明“=>”的实际行为和文档中的描述有所差别(应该属于bug),因此要非常小心。不仅仅是0,所有合法的数值放在“=>”左边时都不会被解释为字符串,为了以防万一,在你确实需要字符串的地方最好使用引号强迫解释器将其作为字符串看待。
23.Whatdoesthisprint?
$a[0]=7;
$a[1]=8;
@b=(5x@a);
@c=((5)x@a);
print"@b@c";
“x”算符为右操作数提供了标量环境,而数组@a在标量环境中求值就是其长度2。当左操作数为标量时“x”的结果是字符串,为列表时结果也是列表。故最终:
@b=("55");
@c=(5,5);
显示结果为“5555”。
24.Whatdoesthisdo?
$print_blanks=1;
while(<>){
nextunlesslength&$print_blanks;
print;
}
典型问题,length同单目运算符类似,对参数“获取”能力非常强,未给出参数时它会尝试将后续表达式结合求值,因此解释为length(&$print_blanks),产生运行时错误。让length使用默认参数$_最好用length()的形式。
25.Whatdoesthisdo?
$n=5;
while(<>){
if(1..$n){print;}
}
根据perldocperlop中描述的特性,在标量环境中“..”操作符在左操作数为真时变为真,而在右操作数变为真之后变为假且一直维持该状态。由于题目中1和$n的值都是真,因此只有在首次求值时“..”返回真值,后面一直维持假,故只打印出来读入的第一行内容。
26.Whatdoesthisdo?
$sos=1_1_1xxx1_1_1...1_1_1xxx1_1_1;
首先数字之间的下划线可以忽略,其次“x”算符是左结合的,它的右操作数始终被转换为一个整数,若右操作数小于等于0则返回空字符串,因此该语句变成:
$sos=(111xx)x111...(111xx)x111;
裸字“x”被当作字符串看待,随后转换为整数0,因此“111xx”就是“111x0”,结果是空字符串。故...算符的两个操作数都是空字符串,因此$sos就被赋成空字符串。