15 Biến đổi dữ liệu khác

Một phần của tài liệu Perl và các khái niệm cơ bản (Trang 123 - 133)

Biến đổi dữ liệu khác

Tìm một xâu con

Tìm một xâu con phụ thuộc vào nơi bạn mất nó. Nếu bạn ngẫu nhiên mất nó bên trong một xâu lớn hơn, thì bạn còn may mắn, vì index() có thể giúp bạn tìm rạ Sau đây là dáng vẻ của nó:

$x = index ($string, $substring) ;

Perl định vị lần xuất hiện đầu tiên của substring bên trong string, cho lại một số nguyên chỉ vị trí của kí tự đầu tiên. Giá trị chỉ số này đợc cho lại dựa trên không - tức là nếu tìm đợc substring ở chỗ bắt đầu của string, thì bạn nhận đợc 0. Nếu nó là ở một kí tự sau đó thì bạn nhận đợc 1, và cứ nh thế. Nếu không tìm thấy substring

trong string, thì bạn nhận đợc -1. Ta hãy xem những điều sau:

$where = index(“hello”, “e”); # $where nhận 1 $oerson = “barney”;

$where = index(“fred barney”, $person) ; # $where nhận 5 @rockers = (“fred”, “barney”) ;

$where = index(join(“ “, @rockers), $person); # cũng thế

Chú ý rằng cả hai xâu này đều đợc tìm kiếm và xâu đợc tìm kiếm thì có thể là một hằng xâu kí hiệu, một biến vô hớng có chứa một xâu, hay thậm chí một biểu thức có giá trị xâu vô hớng. Sau đây là một số thí dụ nữa:

$which = index(“a very long string”, “long”); # $switch nhận 7 $which = index(“a very long string”, “lame”); # $switch nhận -1

Nếu xâu có chứa xâu con tại nhiều vị trí thì toán tử index() sẽ cho lại vị trí bên trái nhất. Để tìm ra các vị trí sau, bạn có thể cho index() tham biến thứ bạ Tham biến này là giá trị tối thiểu mà index() sẽ cho lại, cho phép bạn tìm lần xuất hiện tiếp của xâu con theo sau một vị trí đã chọn. Nó trông tựa nh thế này:

$x = index($bigtring, $littlestring, $skip);

Sau đây là một số thí dụ về cách tham biến thứ ba làm việc: $where = index(“hello world”, “l”); # cho lại 2 (l đầu tiên)

$where = index(“hello world”, “l”, 0); # cũng thế $where = index(“hello world”, “l”, 1); # vẫn thế

$where = index(“hello world”, “l”,3); # bây giờ cho lại 3 # (3 là vị trí đầu tiên lớn hơn hay bằng 3)

$where = index(“hello world”, “o”, 5); # cho lại 7 (0 thứ hai) $where = index(“hello world”, “o”, 8); # cho lại -1 (hết sau 8)

Đi theo lối khác, bạn có thể nhòm từ bên phải để có đợc sự xuất hiện bên phải nhất bằng việc dùng rindex(). Giá trị cho lại vẫn là số các kí tự giữa đầu bên trái của xâu và chỗ bắt đầu của xâu con, nh trớc, nhng bạn sẽ nhận đợc sự xuất hiện về bên phải nhất thay vì sự xuất hiện bên trái nhất nếu có nhiều sự xuất hiện. Toán tử rindex() cũng nhận tham biến thứ ba giống nh index() , để cho bạn có thể có đợc sự xuất hiện ít hơn hay bằng vị trí đã chọn. Sau đây là một số thí dụ về điều bạn nhận đợc:

$w = rindex(“hello world”, “he”); # $w nhận 0

$w = rindex(“hello world”, “l”); # $w nhận 9 (l bên phải nhất) $w = rindex(“hello world”, “o”); # $w nhận 7

$w = rindex(“hello world”, “o ”); # $w nhận 4

$w = rindex(“hello world”, “xx”); # $w nhận -1 (không thấy) $w = rindex(“hello world”, “o”, 6); # $w nhận 4 (đầu tiên trớc 6) $w = rindex(“hello world”,“o”, 3); # $w nhận -1 (không thấy trớc 3)

Trích và thay thế một xâu con

Việc lấy ra một mẩu của xâu có thể đợc thực hiện bằng việc áp dụng cẩn thận các biểu thức chính qui, nhng nếu mẩu này bao giờ cũng ở tại một vị trí kí tự đã biết, thì việc này là không hiệu quả. Thay vì vậy, bạn nên dùng substr(). Toán tử này nhận ba đối: một giá trị xâu, một vị trí bắt đầu (đợc đo tựa nh nó đã đợc đo cho index()), và một chiều dài, giống nh:

$s = substr ($string, $start, $length);

Vị trí bắt đầu làm việc giống nh index; kí tự đầu tiên là không, kí tự thứ hai là một, và cứ thế. Chiều dài là số các kí tự cần nắm lấy tại điểm đó: chiều dài bằng không có nghĩa là không có kí tự nào, bằng một có nghĩa là lấy kí tự đầu tiên, bằng hai có nghĩa là hai kí tự, và vân vân. (Nó dừng lại tại cuối xâu, cho nên nếu bạn tìm kiếm quá nhiều, thì cũng không sao cả.) Nó trông giống thế này:

$hello = “hello, world!”;

$grab = substr($hello, 3, 2); # $grap nhận “lo”

$grab = substr($hello, 7, 100); # 7 đến cuối, hay “world!”

Bạn thậm chí có thể tạo ra toán tử “nâng lên luỹ thừa mời” cho các số mũ nguyên nhỏ, nh trong:

$big = substr(“10000000000”, 0, $power+1); # 10**$power

Nếu số đếm các kí tự là không hay bé hơn không, thì một xâu rỗng sẽ đợc cho lạị Mặt khác, nếu vị trí bắt đầu là bé hơn không thì vị trí bắt đầu đợc tính theo số các kí tự từ cuối xâụ Cho nên giá trị -1 đối với vị trí bắt đầu và 1 (hay nhiều) đối với chiều dài sẽ cho bạn kí tự cuốị Tơng tự, -2 cho vị trí bắt đầu với kí tự thứ hai kể từ cuốị Giống thế:

$stuff = substr(“a very long string”, -3, 3); # ba kí tự cuối $stuff = substr(“a very long string”, -3, 1); # kí tự “i”

Nếu vị trí bắt đầu là trớc chỗ mở đầu của xâu (giống nh một số âm khổng lồ lớn hơn chiều dài của xâu), thì chỗ mở đầu sẽ là vị trí bắt đầu (dờng nh bạn đã

dùng 0 làm vị trí bắt đầu). Nếu vị trí bắt đầu là một số dơng khổng lồ, thì xâu rỗng bao giờ cũng đợc cho lạị Nói cách khác, nó có thể làm điều bạn trông đợi nó phải làm, chừng nào bạn còn trông đợi thì nó bao giờ cũng cho lại một cái gì đó khác hơn là một lỗị

Bỏ đi đối chiều dài thì cũng hệt nh bạn đã đa một số khổng lồ vào cho đối đó - nắm lấy mọi thứ từ vị trí đã chọn cho tới cuối xâu* .

Nếu đối thứ nhất của substr() là một biến (nói cách khác, nó có thể xuất hiện bên vế trái của toán tử gán), thì bản thân substr() cũng có thể xuất hiện ở vế bên trái của toán tử gán. Điều này trông có vẻ kì lạ nếu bạn bắt nguồn từ nền tảng C, nhng nếu bạn đã chơi với vài dị bản của BASIC thì nó hoàn toàn là thông thờng.

Điều nhận đợc sự thay đổi nh kết quả của phép gán nh thế là phần của xâu sẽ đợc cho lại, mà có substr() đợc dùng trong một biểu thức. Nói cách khác, substr($var, 3, 2) cho lại kí tự thứ t và thứ năm (bắt đầu từ 3, vì số đếm 2), cho nên việc gán điều đó làm thay đổi hai kí tự này cho $var. Giống nh:

$hw = “hello world!”;

substr($hw, 0, 5) = “howdy”; # $hw bây giờ là “howdy world!”

Chiều dài của văn bản thay thế (cái nhận đợc việc gán vào trong substr) không phải là cùng nh văn bản đợc thay thế, nh trong thí dụ nàỵ Xâu này sẽ tự động tăng trởng hay co lại khi cần để điều hào với văn bản. Sau đây là một thí dụ về việc xâu thành ngắn hơn:

substr($hw, 0, 5) = “hi”; # $hw bây giờ là “hi world!” và đây là một xâu thành dài hơn:

substr($hw, -6, 5) = “worldwide news”; # thay thế “world”

Việc co lại hay dãn ra thì khá hiệu quả, cho nene bạ nđừng lo lắng về việc dùng chúng một cách bất kì, mặc dầu việc thay thế một xâu bằng một xâu chiều dài tơng đơng thì vẫn nhanh hơn nếu bạn có cơ hộị

Định dạng dữ liệu bằng sprintf()

Toán tử printf đôi khi cũng dễ sử dụng khi đợc dùng để lấy một danh sách các giá trị và tạo ra một dòng ra cho hiển thị các giá trị đó theo cách điều khiển đợc. Toán tử sprintf() là đồng nhất với printf về các đối, nhng cho lại bất kì cái gì đã đợc printf đa ra nh một xâu riêng biệt. (hãy nghĩ về điều này nh “xâu printf”.) Chẳng hạn, để tạo ra một xâu bao gồm chữ X theo sau bởi một giá trị năm chữ số lấp đầy bởi không của $y, cũng dễ dàng nh:

$result = sprintf(“X%05d”, $y);

Nếu bạn không quen thuộc với xâu định dạng của printf và sprintf, thì hãy tham khảo vào tài liệu printf hay sprintf. (Tên sprintf, trong thực tế có nguồn gốc từ trình th viện cùng tên.)

* Các bản Perl cổ hơn không cho phép bỏ đi đối thứ ba, dẫn tới việc những lập trình viên Perl tiền phong đã dùng số khổng lồ cho đối đó. Bạn có thể vợt qua điều này trong cuộc hành trình khảo cổ Perl của mình.

Sắp xếp nâng cao

Trớc đây, bạn đã biết rằng bạn có thể lấy một danh sách rồi sắp xếp nó theo thứ tự ASCII tăng dần (nh các xâu), bằng cách dùng toán tử sort có sẵn. Điều gì sẽ xẩy ra nếu bạn không muốn sắp xếp theo thứ tự ASCII tăng dần, mà thay vì thế là một cái gì đó khác, kiểu nh sắp xếp số? Đợc, Perl cho bạn công cụ bạn cần để làm việc nàỵ Trong thực tế, bạn sẽ thấy rằng sort của Perl là hoàn toàn tổng quát và có thể thực hiện bất kì thứ tự sắp xếp nàọ

Để định nghĩa việc sắp xếp theo mầu sắc khác, bạn cần định nghĩa một trình so sánh mà mô tả cho cách so sánh hai phần tử. Tại sao điều này lại cần thiết? Đ- ợc, nếu bạn nghĩ về nó, thì sắp xếp là việc đặt một bó các thứ theo một trật tự so sánh tất cả chúng với nhaụ Vì bạn không thể nào so sánh chúng ngay một lúc nên bạn cần so sánh hai phần tử mỗi lúc, để cuối cùng dùng cái bạn phát hiện ra về thứ tự cho từng cặp mà đặt toàn bộ cả lũ vào hàng.

Trình so sánh đợc định nghĩa nh một trình thông thờng. Trình này sẽ đợc gọi đi gọi lại, mỗi lần lại truyền hai phần tử của danh sách cần đợc sắp. Trình này phải xác định liệu giá trị thứ nhất là bé hơn, bằng hay lớn hơn giá trị thứ hai, và cho lại một giá trị mã hoá (sẽ đợc mô tả ngay sau đây). Tiến trình này đợc lặp lại cho tới khi danh sách đợc sắp hoàn toàn.

Để tiết kiệm tốc độ thực hiện một chút, hai giá trị này không đợc truyền trong ảng, mà thay vì thế đợc trao cho một trình con nh giá trị của biến toàn cục $a và $b. (Bạn đừng lo - giá trị nguyên thuỷ của $a và $b đợc bảo vệ an toàn.) Trình này nên cho lại một giá trị âm nào đó nếu $a “bé hơn’ $b, bằng không nếu $a “bằng” $b, và bất kì số dơng nào nếu $a “lớn hơn” $b. Bây giờ bạn hãy nhớ, “bé hơn” là t- ơng ứng với ý nghĩa của bạn về “bé hơn” - nó có thể là một so sánh số, tơng ứng với kí tự thứ ba của xâu, hay thậm chí tơng ứng với giá trị của bất kì mảng nào có dùng các giá trị đợc truyền vào nh khoá. Đấy mới thực sự là mềm dẻọ

Sau đây là một thí dụ về một chơng trình con sắp xếp mà sẽ sắp mọi thứ theo thứ tự số: sub by_number { if ($a < $b) { -1; } elsif ($a == $b) { 0; } elsif ($a > $b) { 1; } }

Bạn hãy chú ý đến tên by_number. Không có gì đặc biệt về cái tên của trình con này, nhng bạn sẽ thấy tại sao tôi lại thích cái tên bắt đầu bởi by_ trong giây lát.

Ta hãy nhìn qua trình con nàỵ Nếu giá trị của $a là bé hơn (về mặt số trong tr- ờng hợp này) giá trị của $b, thf ta cho lại giá trị -1. Nếu các giá trị là bằng nhau về con số thì ta cho lại không, còn ngoài ra cho lại 1. Vậy, theo mô tả của ta cho trình so sánh sắp xếp thì điều này sẽ làm việc.

Làm sao ta dùng đợc nó? Ta hãy thử sắp xếp danh sách saụ $somelist = (1,2,4,8,16,32,64,128,256);

Nếu ta dùng sort thông thờng không sang sửa lại danh sách thì ta sẽ đợc các số sắp nh chúng là các xâu, và theo trật tự ASCII, tựa nh:

@wronglist = sort @somelist;

# @wronglist bây giờ là (1,128,16,2,256,32,64,8)

Chắc chắn đấy không phải là sắp xếp số. Thôi đợc, ta sẽ cho sort một trình sắp xếp đợc định nghĩa mớị Tên của trình sắp xếp đi ngay sau từ khoá sort, nh:

@rightlist = sort by_number @wronglist; # @rightlist bây giờ là (1,2,4,8,32,64,128,256)

Đây quả là mẹọ Chú ý rằng bạn có thể đọc sort với trình con sắp xếp đi kèm theo kiểu con ngời: “sắp xếp theo số”. Đó là lí do tại sao tôi đặt tên cho trình con với tiền tố by_.

Nếu bạn cũng thiên về nh thế và muốn nhấn mạnh rằng cái đi sau sort là một trình con, thì bạn có thể đặt trớc nó bằng một dấu và (&), khi đó Perl sẽ không bận tâm nữa, nhng nó đã biết rằng cái nằm giữa từ khoá sort và danh sách phải là một tên trình con.

Cái loại giá trị ba ngả kiểu -1, 0, 1 dựa trên cơ sở so sánh số thờng xuất hiện trong trình con so sánh đến mức Perl có một toán tử đặc biệt để làm điều này trong một lần. nó thờng đợc gọi là toán tử tầu vũ trụ, và trông giống <=>. Dùng toán tử tầu vũ trụ này, trình con sắp xếp trên đây có thể đợc thay thế bằng:

sub by_number { $a <=> $b; }

Bạn hãy chú ý đến con tầu vũ trụ giữa hai biến nàỵ Quả vậy, nó thực là toán tử dài ba kí tự. Con tầu vũ trụ cho lại cùng giá trị nh dây chuyền if/elsif trong định nghĩa trớc của trình nàỵ Bây giờ điều này là có ngắn gọn, nhng bạn có thể viết tắt lời gọi sắp xếp thêm nữa, bằng việc thay thế tên của trình sắp xếp bằng toàn bộ trình sắp xếp trong dòng, giống nh:

@rightlist = sort { $a <=> $b } @wronglist;

Một số ngời biện minh rằng điều này làm giảm tính dễ đọc. Một số khác thì lại biện minh rằng nó loại bỏ nhu cầu phải đi đâu đó để tìm ra định nghĩạ Perl chẳng quan tâm đến điều đó. Qui tắc cá nhân của tôi là ở chỗ nếu nó không khớp trên một dòng hay tôi phải dùng nó nhiều lần, thì nó nên thành một trình con.

Toán tử tầu vũ trụ dành cho so sánh số, có toán tử xâu so sánh gọi là cmp. Toán tử cmp cho lại một trong ba giá trị tuỳ theo việc so sánh xâu tơng đối của hai đốị Cho nên, một cách khác để viết trật tự mặc định là:

@result = sort { $a cmp $b } @somelist;

Có lẽ bạn còn cha hề viết trình con đích xác này (bắt chớc sắp xếp mặc định có sẵn), chừng nào bạn còn cha viết một cuốn sách về Perl. Tuy thế, toán tử cmp

vẫn có ích lợi của nó, trong việc xây dựng các lợc đồ sắp thứ tự theo tầng. Chẳng hạn, bạn không cần đặt các phần tử theo thứ tự số chừng nào chúng không bằng nhau về mặt số, và trong trờng hợp đó chúng phải đợc sắp theo trật tự ASCIỊ (Theo ngầm định, trình by_number trên sẽ chỉ dùng cho các xâu phi số theo một trật tự ngẫu nhiên nào đó vì không có trật tự số khi so sánh hai giá trị không.) Sau đây là một cách nói “cần so sánh theo số, chừng nào chúng cha bằng nhau về số, còn thì so sánh theo xâu”:

sub by_mostly_number { ($a <=> $b) || ($a cmp $b); }

Điều này làm việc thế nàỏ Thế này, nếu kết quả của tầu vũ trụ là -1 hay 1 thì phần còn lại của biểu thức bị bỏ qua, và giá trị -1 hay 1 đợc cho lạị Néu tầu vũ trụ tính ra giá trị không thì toán tử cmp trở thành quan trọng, cho lại một giá trị thứ tự thích hợp xem nh giá trị của xâụ

Giá trị đợc so sánh không nhất thiết là giá trị đợc truyền vàọ Chẳng hạn, bạn có một mảng kết hợp trong đó khoá là tên đăng nhập và các giá trị là tên thật của từng ngời dùng. Giả sử bạn muốn in ra một biểu đồ trong đó tên đăng nhập và tên thật đợc cất giữ theo thứ tự tên thật. Bạn sẽ làm điều đó nh thế nàỏ

Thực tại, việc ấy khá dễ dàng. Ta hãy giả sử các giá trị là trong mảng %names. Tên đăng nhập vậy là danh sách của key(%names). Điều ta muốn đạt tới là một danh sách các tên đăng nhập đợc sắp theo giá trị tơng ứng, vậy với bất kì khoá riêng $a nào, ta cần nhìn vào $names{$a} và sắp xếp dựa trên điều đó. Nếu bạn nghĩ về điều này theo cách đó thì nó gần nhu đã tự viết ra rồi, nh trong:

@sortedkeys = sort by_names keys(%names); sub by_names {

$names{$a} cmp $names{$b}; }

foreach (@sortedkeys) {

print “$_ có tên thật là $names{$_}\n”; }

Với điều này tôi cũng đã thêm vào một so sánh. Giả sử tên thật của hai ngời dùng là trùng nhaụ Vì bản chất chợt nẩy ra của trình sort, tôi có thể lấy một giá trị này trớc giá trị kia lần đầu tiên rồi các giá trị theo thứ tự đảo lại cho lần saụ Điều

Một phần của tài liệu Perl và các khái niệm cơ bản (Trang 123 - 133)

Tải bản đầy đủ (PDF)

(139 trang)