1. 程式人生 > 實用技巧 >Perl學習筆記03——列表和陣列

Perl學習筆記03——列表和陣列

列表和陣列

1.列表指的是標量的有序集合,而陣列則是儲存列表的變數。更精確地說,列表指的是資料,而陣列指的是變數。

2.陣列或列表中的每個元素都有相應的整數作為索引,從0開始遞增,每次加1

3.陣列或列表每個元素都是獨立不相關的標量值,所以列表或陣列可能包含數字、字串、undef值或是不同型別標量值的混合。

4.列表的值不一定要放在數組裡,但每個陣列變數都一定包含一個列表(即便是不含任何元素的空列表)。

訪問陣列中的元素

訪問陣列元素:

print $fred[0];

修改陣列元素:

$fred[2] = "diddly";

任何求值能得到數字的表示式都可以用作下標,假如它不是整數,則會自動捨去小數,無論正負。

$number = 2.71828;
print $fred[$number - 1];  #結果和print $fred[1]相同

假如下標超出陣列的末端,則對應的值將會是undef

特殊的陣列索引($#rocks

如果對索引值超過陣列尾端的元素進行賦值,陣列將會根據需求自動擴大,增補元素預設取值為undef

$rocks[0] = 'bedrock';  #一個元素
$rocks[1] = 'slate';  #又一個元素
$rocks[99] = 'schist';  #現在有97個undef元素

最後一個元素的索引值

對陣列rocks而言,最後一個元素的索引值是

$#rocks,但這個數字比陣列元素的個數少1,因為還有一個編號為0的元素。

$end = $#rocks;  #99,也就是最後一個元素的索引值
$number_of_rocks = $end + 1;  #100,陣列元素的個數
print $rocks[$#rocks];  #訪問最後一個元素

負數陣列索引

注意:超出陣列大小的負數索引值只會得到undef,而不會繞回到陣列尾部。

print $rocks[-1];  #和$rocks[$#rocks]一樣,但更簡單
print $rocks[-100];  #得到'bedrock'
$rocks[-200] = 'crystal';  #
致命錯誤!

列表直接量

列表直接量(也就是在程式程式碼中表示一列資料的寫法),可以由小括號內用逗號隔開的一串資料表示,這些資料就稱為列表元素。

(1,2,3)  #包含數字1、2、3這三個數字的列表
(1,2,3,)  #包含數字1、2、3(末尾的逗號會被忽略)
("fred",4.5)  #兩個元素,"fred"和4.5
()  #空列表——零個元素

範圍操作符(..

如果是某種規律的序列,不用逐個鍵入。範圍操作符(..)可用於自動建立它兩側標量值之間的所有值。

(1..100)  #從1到100的整數序列
(1..5)  #等同於(1,2,3,4,5)
(1.7..5.7)  #同上,等同於(1,2,3,4,5),小數點部分去掉後取範圍
(0,2..6,10,12)  #等同於(0,2,3,4,5,6,10,12)

NOTE

1.範圍操作符只能從小到大依次累加

(5..1)  #得到的是空列表

2.列表中的元素可以不必都是常數,它們可以是表示式。

($m,17)
($m+$o, $p+$q)
($m..$n)
(0..$#rocks)  #rocks陣列的下標序列

qw簡寫

qw簡寫可以快速輸入,免除反覆鍵入引號和逗號。

("fred","barney","betty","wilma","dino")
等同於
qw( fred barney betty wilma dino)

NOTE

1.qw構建的列表中,Perl都會將其當成單引號內的字串來處理,所以,不能像雙引號內的字串一樣使用\n$fred其中的空白字元(如空格、製表符以及換行符)會被拋棄,然後剩下的就是列表中的元素

所以,上面列表可以寫成這樣:

qw( fred 
barney     betty 
wilma dino)

2.因為qw算是一種引用的形式,所以不能將註釋放在qw列表中

qw(
    fred  #abc,在這裡加註釋會被當成一個元素
    barney
    betty
    wilma
    dino
)

3.除了圓括號()Perl還允許用任何標點符號作為定界符,也可以是成對符號

qw!fred barney betty wilma dino!
qw/fred barney betty wilma dino/
qw#fred barney betty wilma dino#
qw{fred barney betty wilma dino}
qw[fred barney betty wilma dino]
qw<fred barney betty wilma dino>

注意:

1)如果需要在被圈引的字串內使用定界符,可通過反斜線轉義來引入這個字元。

qw! yahoo\! google ask msn !  #將yahoo!作為一個元素引入

2)和單引號內的字串一樣,兩個連續的反斜線表示一個實際的反斜線。

qw( This is a \\ real backslash);

列表的賦值

▲列表值賦值到變數

($fred, $barney, $dino) = ("flintstone", "rubble", undef);

NOTE

1.如果變數個數>列表值的個數,多出來的值會被忽略掉

($fred, $barney) = qw< flintstone rubble slate granite >  #忽略掉末尾的兩個元素

2.如果變數個數<列表值的個數,多出來的變數會被設成undef(或空列表,後面解釋)

($wilma, $dino) = qw[flinstone];  #$dino的值為undef

▲交換兩個變數的值

($fred, $barney) = ($barney, $fred);
($betty[0], $betty[1]) = ($betty[1], $betty[0]);

構建一個字串陣列

($rock[0], $rock[1], $rock[2], $rock[3]) = qw/talc mica feldspar quartz/;

引用整個陣列(用@字元)

在賦值操作符兩邊都可以用

@rock = qw/bedrock slate lava/;
@tiny = ();  #空列表
@giant = 1..1e5;  #包含100000個元素的列表
@stuff = (@giant, undef, @giant);  #包含200001個元素的列表

注意:

1.

$dino = "granite";
@quarry = (@rock, "crushed rock", @tiny, $dino);

這個賦值運算將會把@quarry的值設成擁有5個元素的列表(bedrock, slate, lava, crushed rock, granite),因為@tiny貢獻了0個元素給這個列表(注意,由於空列表裡沒有任何元素,也就不會有undef被賦值到列表中,如果需要undef,也可以顯式寫明)。

2.

列表中的陣列名會被展開成(它所擁有的)元素列表,因為陣列只能包含標量,不能包含其他陣列,所以陣列無法成為列表中的元素。

3.

就像標量變數的初始值是undef一樣,新的或是空的陣列的初始值是空列表,即()

poppush操作符

pop操作符:提取陣列末尾的元素並將其作為返回值

@array = 5..9;
$fred = pop(@array);  # $fred為9,@array現在是(5,6,7,8)
$barney = pop @array;  # $barney為8,@array現在是(5,6,7)
pop @array;  # @array現在是(5,6),pop的返回值7被拋棄了

NOTE

1.最後一行是在“空上下文”中使用pop操作符。所謂空上下文,只不過是表示返回值無處可去的一種說辭。這其實也是pop操作符常見的一種用法,用來刪除陣列中最後一個元素。

2.如果陣列是空的,pop什麼也不做,直接返回undef

3.pop後面加不加括號都可以。

push操作符:新增一個(或一串)元素到陣列尾端

@array = (5,6);
push (@array, 0);  # @array現在是(5,6,0)
push @array, 8;  # @array現在是(5,6,0,8)
push @array, 1..10  # @array得到了10個新元素
@others = qw/5 2 0 y f b/;
push @array, @others;  # @array又得到了6個新元素(共20個)

注意:

pushpop的第一個引數都必須是要操作的陣列變數,對列表直接量進行壓入(push)或彈出(pop)操作是沒有意義的。(因為對列表直接量進行操作後,處理後的列表又怎麼表示呢?)

shiftunshift操作符

shift操作符:提取陣列開頭的元素並將其作為返回值

@array = qw#dino fred barney#;
$m = shift(@array);  # $m變成"dino",@array現在是("fred", "barney")
$n = shift @arry;  # $n變成"fred",@array現在是("barney")
shift @array;  # 現在@array變空了
$o = shift @arry;  # $o變成undef,@array還是空的

unshift操作符:新增一個(或一串)元素到陣列開頭

unshift(@array, 5);  # @array現在僅包含只有一個元素的列表(5)
unshift @array, 4;  # @array現在是(4,5)
@others = 1..3;
unshift @array, @others;  # @array變成了(1,2,3,4,5)

splice操作符

push-popshift-unshift操作符都是針對陣列首尾的元素操作的,splice操作符是針對陣列中間的元素操作的。

splice含義:刪除操作。

splice可接受4個引數,後兩個引數可選

splice(引數1,引數2,引數3,引數4)

引數1要操作的陣列

引數2要操作的一組元素的開始位置(索引值)

引數3(可選):指定要操作的元素長度(即元素個數)

引數4(可選):要替換的列表

接受兩個引數:

@array = qw(pebbles dino fred barney betty);
@removed = splice @array, 2;  
#在原來的陣列中刪掉從fred(索引值為2)開始往後的元素
#@removed變成qw(fred barney betty)
#原先的@array變成qw(pebbles dino)

NOTE

1.splice返回值是什麼? ——刪除的元素列表

2."原地更新"嗎? ——是

接受三個引數

3個引數作用是指定要操作的元素長度(注意不是結束位置),即元素個數,通過這個引數,可以刪除陣列中間的一個片段

@array = qw(pebbles dino fred barney betty);
@remoed = splice @array, 1, 2;
#刪除dino和fred兩個元素
# @remoed變成qw(dino fred)
# @array變成qw(pebbles barney betty)

接受四個引數

4個引數是要替換的列表,也就是可以補充新元素到原來的陣列中,新加入列表的長度不一定要和拿走的元素片段長度一樣。

@array = qw(pebbles dino fred barney betty);
@remoed = splice @array, 1, 2, qw(wilma);
#刪除dino和fred
# @remoed變成qw(dino fred)
# @array變成qw(pebbles wilma barney betty)

特殊用法

實際上,新增元素列表並不需要預先刪除某些元素,把表示長度的第三個引數設為0,既可不加刪除地插入新列表。

@array = qw(pebbles dino fred barney betty);
@remoed = splice @array 1, 0, qw(wilma);
#什麼元素都不刪除
# @remoed變成()
# @array變成qw(pebbles wilma dino fred barney betty)

注意:

wilma出現在dino 之前的位置上,Perl從索引值1的地方插入新列表,然後順移原來的元素

字串中的陣列內插

和標量一樣,陣列的內容同樣可以被內插到雙引號引起的字串中。內插時,會在陣列的各個元素之間自動新增分隔用的空格

@rocks = qw( flinstone slate rubble);
print "quartz @rocks limestone\n";  #列印5個以空格隔開的單詞

NOTE

1.陣列被內插後,首尾都不會增添額外空格,若真的需要,可以自己手動新增

2.如果@為雙引號內字串的內容本身時,需引入反斜線轉義\@或直接用單引號來定義字串

$email = "[email protected]";  #錯!這樣會內插@bedrock這個陣列
$email = "fred\@bedrock.edu";  #正確
$email = '[email protected]';  #另一種寫法,效果相同

3.內插陣列中的某個元素時,會被替換成該元素的值。

@fred = qw(hello dolly);
$y = 2;
$x = "This is $fred[1]'s place";  #得到This is dolly's place
$x = "This is $fred[$y-1]'s place";  #效果同上

4.如果要在某個標量變數後面緊接著寫左方括號,需要隔離,否則會被識別為陣列引用。

@fred = qw(eating rocks is wrong);
$fred = 'right';
print "this is $fred[3]\n";  #用到了$fred[3],列印"wrong"
print "this is ${fred}[3]\n";  #列印right[3](用花括號避開歧義)
print "this is ${fred}"."[3]\n";  #列印right[3](用分開的字串)
print "this is $fred\[3]\n";  #列印right[3](用反斜線避開歧義)

foreach控制結構

foreach迴圈能逐項遍歷列表中的值,依次提取使用:

foreach $rock(qw/ bedrock slate lava /){
    print "One rock is $rock.\n";  #依次列印這三個單詞    
}

NOTE

1.每次迴圈迭代時,控制變數$rock都會從列表中取得新值。

2.控制變數並不是列表元素的複製品,實際上,它就是列表元素本身。也就是說,假如在迴圈體中修改了控制變數的值,也就同時修改了列表元素。

@rocks = qw/ bedrock slate lava /;
foreach $rock(@rocks){
    $rock = "\t$rock";  #在@rocks的每個元素前加上製表符
    $rock .= "\n";  #同時在末尾加上換行符
}
print "The rocks are:\n",@rocks;  #各自佔一行,並使用縮排(注意這種寫法,從第二個列表元素開始前面都帶一個空格)

注意:

@rocks = qw(a b c d);
print @rocks;  #會得到abcd
print "@rocks";  #會得到a b c d

迴圈結束後,控制變數的值仍然是迴圈執行之前的值。Perl會自動儲存foreach迴圈的控制變數並在迴圈結束後還原。

在迴圈執行期間,我們無法訪問或改變已儲存的值,所以當迴圈結束時,變數仍讓保持迴圈前的值。

如果它之前從未被賦值,那就仍然是undef

也就是說,如果想把迴圈的控制變數取名為$rock的話,不必擔心之前是否用過同名的變數。

$rock = 'shale';
@rocks = qw/ bedrock slate lava /;
foreach $rock(@rocks){
    ...
}
print "rock is still $rock\n";  #列印'rock is still shale'

Perl的預設變數:$_

假如在foreach迴圈開頭省略控制變數,Perl就會用它最喜歡用的預設變數$_

這個變數名除了名稱比較特別以外,和其它標量變數幾乎沒什麼差別。

foreach (1..10){  #預設會用$_作為控制變數
    print "I can count to $_!\n";
}

Perl有許多預設變數,$_是最常用的一個,如果不加說明,Perl預設使用$_

$_ = "Yabba dabba doo\n";
print;  #預設列印$_

reverse操作符

reverse操作符會讀取列表的值(一般來自陣列),並按相反次序返回新的列表。

@fred = 6..10;
@barney = reverse(@fred);  #返回10,9,8,7,6
@wilma = reverse 6..10;  #同上,但無需額外的陣列
@fred = reverse @fred;  #倒序後儲存到原來的陣列

注意:

reverse只是返回倒序後的列表,它不會修改給它的引數。加入返回值無處可去,那這種操作也就變得毫無意義。

reverse @fred;  #錯誤的用法,這不會使陣列內容倒序
@fred = reverse @fred;  #正確用法

sort操作符

sort操作符會讀取列表的值(一般來自陣列),依次按字元的內部編碼順序對它們排序。

排序按照ASCII碼的大小進行。

@rocks = qw/ bedrock slate rubble granite /;
@sorted = sort(@rocks);  #返回bedrock,granite,rubble,slate
@back = reverse sort @rocks;  #逆序,從 slate 到 bedrock 排列
@rocks = sort @rocks;  #將排序後的結果存到原陣列@rocks
@numbers = sort 97..102;  #得到100,101,102,97,98,99

NOTE

1.按預設排序規則,任何以1開頭的字串會被排在以9開頭的字串之前。

2.排序操作和reverse操作一樣,不會修改原始引數,只是返回新的列表所以要對陣列排序,就必須將排序後的結果存回陣列。

each操作符

Perl 5.12開始,已經可以針對陣列使用each操作符了。在這之前each只能用於提取雜湊的鍵-值對。

每次對陣列呼叫each,會返回陣列中下一個元素對應的兩個值——陣列索引與元素值:

require v5.12;
@rocks = qw/ bedrock slate rubble granite /;
while (($index, $value) = each @rocks){
    print "$index: $value\n";
}

注意:

這裡使用了require,因為使用use v5.12會預設啟用“嚴格(strict)”模式。

如果不用each,就得根據索引從小到大遍歷,然後藉助索引取得元素值。

@rocks = qw/ bedrock slate rubble granite /;
foreach $index(0..$#rocks){
    print "$index: $rocks[$index]\n";
}