Dữ liệu mảng và danh sách
Mảng là gì?
Mảng là một danh sách có thứ tự các dữ liệu vô hớng. Mỗi phần tử của mảng đều
là một biến vô hớng tách biệt với một giá trị vô hớng độc lập. Các giá trị này là đợc
sắp thứ tự - tức là chúng có một trình tự đặc biệt từ phần tử thấp nhất đến cao nhất.
Mảng có thể có bất kì số phần tử nào. Mảng nhỏ nhất không có phần tử nào, trong
khi mảng lớn nhất thì có thể lấp kín toàn bộ bộ nhớ có sẵn. Một lần nữa, điều này lại
đợc giữ hợp với triết lí của Perl về không có giới hạn không cần thiết nào.
Biểu diễn hằng kí hiệu
Một hằng kí hiệu mảng (cách thức bạn biểu diễn giá trị của một mảng bên trong
chơng trình mình) là một danh sách các giá trị tách nhau bằng dấu phẩy và đợc bao
trong dấu ngoặc tròn. Những giá trị này tạo nên các phần tử của danh sách. Chẳng
hạn:
(1,2,3) # mảng gồm ba giá trị 1, 2 và 3
(fred, 4.5) # hai giá trị, fred và 4.5
Các phần tử của mảng không nhất thiết là hằng - chúng có thể là biểu thức mà sẽ
đợc tính mới lại mỗi lần hằng đợc sử dụng. Chẳng hạn:
($a, 17) # hai giá trị: giá trị hiện tại của $a, và 17
($b+$c,$d+$e) # hai giá trị
Mảng rỗng (mảng không có phần tử nào) đwjc biểu diễn bằng một cặp dấu ngoặc
rỗng:
() # mảng rỗng (không phần tử)
Một phần tử của mảng có thể bao gồm toán tử cấu tử mảng, đợc chỉ ra bởi hai
giá trị vô hớng tách nhau bởi hai dấu chấm liên tiếp. Toán tử này tạo ra một danh sách
các giá trị bắt đầu tại giá trị vô hớng bên trái kéo cho tới gía trị vô hớng bên phải, mỗi
lần tăng lên một. Chẳng hạn:
(1..5) # giống nh (1, 2, ,3 ,4, 5)
(1.2..5.2) # giống nh (1.2, 2.2, 3.2, 4.2, 5.2)
(2..6,10,12) # giống nh (2,3,4,5,6,10,12)
($a..$b) # phạm vi đợc xác định bởi giá trị hiện tại của $a và $b
Nếu giá trị vô hớng bên phải bé hơn vô hớng bên trái thì sẽ tạo ra danh sách rỗng
- bạn không thể đếm ngợc trật tự của các giá trị. Nếu giá trị cuối cùng không phải là
toàn bộ số bớc trên giá trị ban đầu thì danh sách sẽ dừng chỉ ngay trớc giá trị tiếp mà
sẽ vợt ra ngoài phạm vi:
(1.3..6.1) # giống nh (1.3, 2.3, 3.3, 4.3, 5.3)
Một cách dùng của hằng kí hiệu mảng là nh đối của toán tử print() đã đợc giới
thiệu trớc đây. Các phần tử của danh sách này đợc in ra mà không có bất kì khoảng
trống xen thêm vào:
print (Câu trả lời là , $a, \n) ; # ba phần tử mảng hằng kí hiệu
Câu lệnh này in ra Câu trả lời là, theo sau bởi một dấu cách, giá trị của $a, và
dấu dòng mới. Ta hãy chuyển sang cách dùng khác cho hằng kí hiệu mảng.
Biến
Một biến mảng giữ một giá trị mảng riêng (không hay nhiều giá trị vô hớng). Các
tên biến mảng là tơng tự với các tên biến vô hớng, chỉ khác kí tự khởi đầu, là một dấu
@ chứ không phải là dấu đô la $. Chẳng hạn:
@fred # biến mảng @fred
@A_Very_Long_Array_Variable_Name
@A_Very_Long_Array_Variable_Name_that_is_different
Lu ý rằng biến mảng @fred là không có quan hệ gì theo bất kì cách nào với biến
vô hớng $fred. Perl duy trì không gian tên tách biệt cho các kiểu đối tợng khác nhau.
Giá trị của một biến mảng mà cha đợc gán là (), danh sách rỗng.
Một biểu thức có thể tham khảo tới các biến mảng nh một tổng thể, hoặc nó có
thể xem xét và thay đổi từng phần tử của mảng đó.
Toán tử
Các toán tử mảng hành động trên các mảng nh một tổng thể. Một số toán tử mảng
có thể cho lại một giá trị mảng, mà có thể hoặc đợc dùng nh một giá trị cho toán tử
mảng khác, hoặc đợc gán vào một biến mảng khác.
Phép gán
Có lẽ toán tử mảng quan trọng nhất là toán tử gán mảng, cho mảng một giá trị.
Nó là dấu bằng, giống nh toán tử gán vô hớng. Perl xác định liệu phép gán có là phép
gán vô hớng hay phép gán mảng bằng việc để ý xem liệu phép gán là cho biến vô h-
ớng hay mảng
*
. Chẳng hạn:
@fred = (1,2,3); # mảng fred nhận ba phần tử hằng kí hiệu
@barney = @fred; # bây giờ đợc sao sang @barney
Nếu một giá trị vô hớng đợc gán vào trong một biến mảng thì giá trị vô hớng trở
*
*
Điều này áp dụng cho lvalue vô hớng hay mảng cũng nh các biến đơn
thành phần tử duy nhất của mảng:
@huh = 1; # 1 đợc đặt cho danh sách (1) một cách tự động
Tên biến mảng có thể xuất hiện trong danh sách hằng kí hiệu mảng. Khi giá trị
của danh sách đợc tính thì Perl thay thế tên biến mảng bằng giá trị hiện tại của mảng
đó, giống vậy:
@fred = (một, hai);
@barney = (4,5,@fred, 6, 7); @barney trở thành
(4,5,một,hai,6,7)
@barney = (8, @barney); # đặt 8 vào trớc @barney
@barney = (@barney, cuối); # và cuối là ở cuối
# @barney bây giờ là (8,4,5,một,hai,6,7,cuối)
Lu ý rằng các phần tử mảng đợc thêm vào đều ở cùng mức nh phần còn lại của
hằng kí hiệu - một danh sách không thể chứa một danh sách khác nh một phần tử
*
.
Nếu một mảng hằng kí hiệu chỉ chứa các tham khảo biến (không phải là biểu
thức) thì mảng hằng kí hiệu ấy cũng có thể đợc xử lí nh một biến. Nói cách khác, một
mảng hằng kí hiệu nh thế có thể đợc dùng ở vế bên trái của phép gán. Mỗi biến vô h-
ớng trong mảng kí hiệu nhận một giá trị tơng ứng từ danh sách ở vế phải của phép
gán. Chẳng hạn:
($a, $b, $c) = (1, 2, 3); # đặt 1 cho $a, 2 cho $b, 3 cho $c
($a, $b) = ($b, $a); # tráo đổi $a và $b
($d, @fred) = ($a, $b, $c); # đặt $a cho $d, và ($b,$c) cho @fred
($e,@fred) = @fred; # loại bỏ phần tử thứ nhất của @fred là $e
# điều này làm cho @fred = ($c) và $e = $b
Nếu số phần tử đợc gán không sánh đúng với số các biến để giữ các giá trị thì
mọi giá trị vợt quá (ở vế phải của dấu bằng) đều im lặng bị loại bỏ, và bất kì biến vợt
quá nào (ở vế trái của dấu bằng) đều đợc cho giá trị undef.
Một biến mảng xuất hiện trong danh sách mảng hằng kí hiệu đều phải ở cuối, vì
biến mảng là tham lam, và nó tiêu thụ tất cả các giá trị còn lại. (Này, bạn có thể đặt
các biến khác sau nó, nhng chúng sẽ chỉ nhận giá trị undef mà thôi.)
Nếu một biến mảng đợc gán cho một biến vô hớng thì số đợc gán là chiều dài của
mảng, nh trong:
@fred = (4, 5, 6); # khởi đầu @fred
$a = @fred; # $a nhận phần tử đầu tiên của @fred
Chiều dài cũng đợc cho lại nếu một tên biến mảng đợc dùng trong hầu hết mọi
chỗ mà một giá trị vô hớng đang đợc cần tới. (Trong mục dới đây có tên Hoàn cảnh
*
*
Perl 5.0 cho phép một tham khảo danh sách là một phần tử danh sách, nhng đấy vẫn không phải là danh sách nh
một phần tử danh sách
vô hớng mảng, chúng ta sẽ thấy rằng điều này quả là đợc gọi nh vậy với việc dùng
tên mảng trong hoàn cảnh vô hớng.) Chẳng hạn, để lấy giá trị bé hơn chiều dài mảng
một đơn vị, bạn có thể dùng @fred-1, vì toán tử trừ vô hớng cần các vô hớng cho cả
hai toán hạng của nó. Chú ý điều sau:
$a = @fred; # $a nhận chiều dài của @fred
($a) = @fred; # $a nhận phần tử đầu tiên của @fred
Phép gán đầu tiên là phép gán vô hớng, và do vậy @fred đợc đối xử nh một vô h-
ớng, cho lại chiều dài của nó. Phép gán thứ hai là phép gán mảng (cho dù chỉ một giá
trị là cần tới), và do vậy cho phần tử đầu tiên của @fred, im lặng bỏ đi tất cả phần còn
lại.
Giá trị của phép gán mảng là chính bản thân giá trị mảng, và có thể đợc xếp tầng
nh bạn có thể làm với các phép gán vô hớng. Chẳng hạn:
@fred = (@barney = (2,3,4)); # @fred và @barney nhận (2,3,4)
@fred = @barney = (2,3,4); # cùng điều ấy
Thâm nhập phần tử
Cho tới nay, chúng ta vẫn xử lí mảng nh một tổng thể, thêm vào và bỏ bớt các giá
trị bằng việc thực hiện gán mảng. Nhiều chơng trình có ích đã đợc xây dựng dùng
mảng mà thậm chí chẳng thâm nhập vào phần tử mảng nào. Tuy nhiên, Perl cung cấp
toán tử chỉ số truyền thống để tham khảo tới một phần tử mảng theo chỉ số.
Với toán tử chỉ số mảng, các phần tử mảng đều đợc đánh số bằng việc dùng số
nguyên tuần tự, bắt đầu từ không
*
và tăng lên một cho mỗi phần tử. Phần tử đầu tiên
của mảng @fred mà đợc thâm nhập tới là $fred[0]. Chú ý rằng @ trên tên mảng trở
thành $ trên tham khảo phần tử. Đó là vì việc tham khảo tới một phần tử của mảng
xác định ra một biến vô hớng (một phần của mảng), mà có thể hoặc đợc gán cho,
hoặc có giá trị hiện tại của nó đợc dùng trong một biểu thức, kiểu nh:
@fred = (7,8,9);
$b = $fred[0]; # đặt 7 vào $b (phần tử đầu tiên của @fred)
$fred[0] = 5; # bây giờ @fred = (5,8,9)
Cũng có thể thâm nhập tới các phần tử khác dễ tơng tự, nh trong:
$c = $fred[1]; $ đặt 8 cho $c
$fred[2]++; # tăng phần tử thứ ba của @fred
$fred[1] += 4; # cộng 4 vào phần tử thứ hai
($fred[0], $fred[1]) = ($fred[1], $fred[0]); # tráo đổi hai phần tử đầu
Việc thâm nhập vào một danh sách các phần tử từ cùng mảng (nh trong thí dụ
*
*
Cũng có thể thay đổi giá trị chỉ số của phần tử đầ utiên thành một số nào đó khác (nh một) bằng việc đặt giá trị cho
biến $[. Tuy nhiên, làm nh vậy có ảnh hởng toàn cục, mà có thể gây lẫn lộn ngời sẽ bảo trì chơng trình của bạn, và có
thể làm tan vỡ chơng trình bạn nhận đợc từ ngời khác. Do vậy, chúng tôi khuyên bạn nên coi đây là một tính năng
không nên dùng.
cuối) đợc gọi là lát cắt, và thờng xuất hiện đến mức có một cách biểu diễn đặc biệt
cho nó:
@fred[0,1] # hệt nh ($fred[0], $fred[1])
@fred[0,1] = @fred[1,0] # tráo đổi hai phần tử đầu
@fred[0,1,2] = @fred[1,1,1] # làm cho cả 3 phần tử giống phần tử thứ hai
@fred[1,2] = (9,10); # đổi hai giá trị cuối thành 9 và 10
Chú ý rằng lát cắt này dùng tiền tố @ chứ không là $. Điều này là vì bạn đang tạo
ra một biến mảng bằng việc chọn một phần của mảng chứ không phải là biến vô hớng
chỉ thâm nhập vào một phần tử.
Lát cắt cũng làm việc trên danh sách hằng kí hiệu, hay bất kì toán tử nào cho lại
một giá trị danh sách:
@who = (fred,barney,betty,wilma)[2,3] ;
# giống nh @x = (fred,barney,betty,wilma); @who = @x[2,3]
Các giá trị chỉ số trong những thí dụ này là các số nguyên hằng kí hiệu, nhng chỉ
số cũng có thể là bất kì biểu thức nào cho lại một số, mà rồi đợc dùng để chọn phần
tử thích hợp:
@fred = (7,8,9);
$a = 2;
$b = $fred[$a]; # giống $fred[2], hay giá trị 9
$c = $fred[$a-1]; # $c nhận $fred[1], hay 8
($c) = (7,8,9) [$a-1]; # cũng điều đó nhng dùng lát cắt
Vậy chơng trình Perl có thể có việc thâm nhập mảng tơng tự nh các ngôn ngữ lập
trình truyền thống.
ý tởng này về việc dùng một biểu thức cho chỉ số cũng có tác dụng cho các lát
cắt. Tuy nhiên bạn hãy nhớ rằng chỉ số cho lát cắt ;à một danh sách các giá trị, cho
nên biểu thức này là một biểu thức mảng, thay vì là motọ biểu thức vô hớng.
@fred = (7,8,9); # nh trong thí dụ trớc
@barney = (2,1,0);
@backfred = @fred[@barney];
# giống nh @fred[2,1,0], hay ($fred[2],$fred[1],$fred[0]),
# hay (9,8,7)
Nếu bạn thâm nhập vào một phần tử mảng bên ngoài hai đầu của mảng hiện tại
(tức là một chỉ số bé hơn không hay lớn hơn chỉ số của phần tử cuối cùng), thì giá trị
undef sẽ đợc cho lại mà không có lời cảnh báo. Chẳng hạn:
@fred = (1,2,3);
$barney = $fred[7]; # $barney bây giờ là undef