在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>);