在perl中,不少地方会切换上下文。所谓上下文,它的重点在于同一个表达式出如今不一样地方,获得的结果不一样。换句话说,同一个表达式,它表达的值不是固定的。这就像是同一个单词,在不一样语境下的意思不一样。python
例如,运算操做符决定数值是一个数字仍是一个字符串。shell
2 * 3 2 x 3
2 * 3
中的2和3都是数值,由于操做符*
是算术运算符,它要求两边都是数字。而2 x 3
中的2是字符串,3是数字,由于操做符x
是这样要求的。数组
还有,对数组@arr
的两种操做:函数
@arr=qw{perl,python,shell}; print @arr,"\n"; # 返回:perlpythonshell print @arr."\n"; # 返回:3
使用逗号分隔@arr
和\n
是产生一个列表,这时的@arr
会替换为该数组中的元素值。使用点号链接@arr
和\n
,这时点号要求两边的都是字符串,数组在这种环境下(标量上下文)返回的是它的元素个数,因此@arr
返回一个数值(但实际上是字符串)。操作系统
在perl解析表达式的时候,你要么但愿它返回一个标量,要么但愿它返回一个列表(其实还有不少种上下文,但至今无人知晓有多少种上下文,perl长老团也不知道)。因此perl中常见的两种上下文是:标量上下文和列表上下文,除此以外还有一个很常见的上下文类型:空上下文(void context)。scala
上下文不只决定了表达式的返回结果类型,还限制了某些环境下只能使用某些上下文。好比,须要传递一个列表的时候却传递了一个标量,这可能会报错误。code
例如:排序
42 + something; # 这里的something必须是标量 sort something; # 这里的something必须是列表
数组@arr
为例:内存
@arr=qw(perl shell python); @sorted=sort @arr; # 列表上下文:返回(perl python shell) @num=@arr + 42; # 标量上下文:返回45
即使是赋值这种操做,都有不一样的上下文:字符串
@arr1=@arr; # 列表上下文,赋值@arr给另外一个数组@arr1 $arr1=@arr; # 标量上下文,赋值@arr的元素个数给变量arr1
比较悲剧的是,没法总结一个通用的规则来解释何时用什么上下文,只能经过一些经验来感觉它。
那些操做列表的表达式(如sort)用在标量上下文会如何?没人知道会如何。不一样的操做,返回的内容没有规律可言。
例如,对列表排序的sort操做放在标量上下文只会返回undef,reverse操做放在标量上下文则是返回字符的逆排序(先将全部元素按照字符串格式链接起来,再对总体进行反转)。
@arr=qw(perl python shell); $sorted=sort @arr; # 返回undef $reversed=reverse @arr; # perlpythonshell-->llehsnohtyplrep print $sorted,"\n"; print $reversed,"\n";
如下是常见的上下文:
$var = something; # 标量上下文 @arr = something; # 列表上下文 ($var1,$var2) = something; # 列表上下文 ($var1) = something; # 列表上下文
如下是常见的标量上下文:
$var = something; $arr[3] = something; 123 + something; if (something) {...} wihle(something) {...} $var[something] = something;
如下是常见的列表上下文:
@arr = something; ($var1,$var2) = something; ($var1) = something; push @arr,something; foreach $var (something){...} sort something; reverse something; print something;
须要注意的几点,将数组赋值给标量变量,获得的是数组的长度(元素个数),将列表赋值给标量变量,获得的是最后一个元素,除了最后一个元素外,其它元素都被丢弃,也就是放进了void context。
@arr = qw(a b c d); $x = @arr; # 结果:$x=4 $y = qw(a b c d); # 结果:$y=d,开启了warnings的状况下会警告 ($y) = qw(a b c d); # 结果:$y=d,不会警告 ($a,$b,$c,$d) = qw(a b c d) # 结果:$a=a,$b=b,$c=c,$d=d
这种状况很简单,若是某个操做的返回结果是标量值,但却在列表上下文中,则直接生成一个包含此返回值的列表。
@arr = 6 * 7; # 结果:@arr=(42) @arr = "hello".' '.'world'; # 结果:@arr=("hello world")
但关于undef和空列表有一个陷阱:
@arr1 = undef; @arr2 = ();
上面的undef是一个标量值,因此赋值后@arr1=(undef)
,它不会清空数组;而()
是空列表,它表示未定义的,因此赋值后@arr2
被清空。
有时候若是想要强制指定标量上下文,可使用伪函数scalar进行强制切换,它会告诉perl这里要切换到标量上下文。
@arr=(perl python shell); print "How many subject do you learn?\n"; print "I learn ",@arr," subjects!\n"; # 错误,这里会输出课程名称 print "I learn ",scalar @arr," subjects!\n"; # 正确,这里输出课程数量
另外一种切换为标量上下文的方式是使用~~
,这是两个比特位取反操做,由于比特位操做环境是标量上下文,两次位取反至关于不作任何操做,因此将环境变成了标量上下文。这在写精简程序时可能会用上,并且由于~~
是符号,能够和函数之间省略空格分隔符:
my @arr = qw(Shell Perl Python); print ~~@arr; print~~@arr;
还可使用下面的技巧从列表上下文切换成标量上下文:
$var = () = expr
Perl中赋值操做老是先评估右边,因此上面等价于$var = (() = expr)
。() = expr
表示转换成列表上下文,使得expr以列表上下文的环境工做。最后的赋值操做,因为左边是$var
,会将列表转换成标量上下文。
(若是不理解,暂时跳过)这种技巧比较常见的是$num = () = <>
,由于<>
在不一样上下文环境下工做的方式是不同的,这个表达式表示以列表上下文环境读取全部行,而后赋值给标量,因此赋值给标量的是列表的元素个数,也就是文件的行数。
它等价于$num = @tmp = <>
。并且彻底可使用scalar()替代scalar(@tmp = <>)
。
只有强制切换到标量上下文的伪函数scalar,没有切换到列表上下文的函数。由于根本用不到。
<STDIN>
放在列表上下文时,会 一次性 读取全部输入(文件/键盘等)。一次性意味着大文件须要大量内存,通常400M的文件,perl可能会花上1G内存,由于perl会事先分配好富裕的空间避免过后问题。
例如:
@lines=<STDIN>; foreach (@lines){ print $_; }
在目前来讲,这正是咱们所需的方式。能够读取每行,并对每行进行操做。但由于是一次性读取,对于大文件来讲,这种方法不可取,应该想其它方法。
<STDIN>
放在列表上下文时,它会一行一行读取,直到读取到文件结尾(EOF)。可是,若是是读取键盘输入,如何给出EOF?在Linux中,按下CTRL+D(Windows下是CTRL+Z)便可,默认它会发送EOF给操做系统,操做系统会通知perl到了文件结尾。
由于<STDIN>
读取的每一行中默认就带有换行符,在列表上下文中,一样可使用chomp()函数来去除每一行的换行符。
@lines=<STDIN>; chomp(@lines);
或者采用更简洁的方式:
chomp(@lines=<STDIN>);