Lessons II : Dữ liệu vô hướng là gì?

Perl

2.1 Dữ liệu vô hướng là gì?

Vô hướng là loại dữ liệu đơn giản nhất mà Perl thao tác. Một vô hướng thì hoặc là một số (giống như 4 hay 3.25e20) hay một xâu các kí tự (giống Xin chào hay Gettysburg Address). Mặc dầu bạn có thể nghĩ về số và xâu như những vật rất khác nhau, nhưng Perl dùng chúng gần như là giống nhau, cho nên chúng ta sẽ nghiên cứu cả hai.

Một giá trị vô hướng có thể được tác động bởi các toán tử (giống như phép cộng hay ghép), và kết quả trả về nói chung là một kết quả vô hướng. Một giá trị vô hướng có thể được cất giữ vào trong một biến vô hướng. Các biến vô hướng có thể được đọc từ tệp và thiết bị, và có thể được ghi thiết bị xuất.

2.2 Số

Mặc dầu kiểu vô vô hướng thì hoặc là một số hay một xâu, nhưng cũng vẫn có ích khi ta nhìn vào các số và xâu tách biệt nhau trong một chốc lát. Ta sẽ xét số trước rồi đến xâu…

2.2.1 Tất cả các số đều có cùng định dạng bên trong

Như bạn sẽ thấy trong vài đoạn tiếp đây, bạn có thể xác định cả số nguyên (toàn bộ số, giống như 14 hay 342) và số dấu phẩy động (số thực với dấu chấm thập phân, như 3.14 hay 1.35*1025). Nhưng bên trong, Perl chỉ tính với các giá trị dấu phẩy động độ chính xác gấp đôi. Điều này có nghĩa là không có giá trị nguyên bên trong Perl – một hằng nguyên trong chương trình được xử lí như giá trị dấu phẩy động tương đương. Bạn có lẽ không để ý đến việc chuyển đổi (hay quan tâm nhiều), nhưng bạn nên dừng tìm kiếm phép toán nguyên (xem như ngược với các phép toán dấu phẩy động), vì không có phép tính nào như vậy đâu.

2.2.2 Hằng kí hiệu động

Hằng kí hiệu là một cách để biểu diễn một giá trị trong văn bản chương trình Perl – bạn cũng có thể gọi điều này là một hằng trong chương trình mình, nhưng tôi sẽ dùng thuật ngữ hằng kí hiệu. Hằng kí hiệu là cách thức biểu diễn dữ liệu trong mã chương trình gốc của chương trình bạn như cái vào cho trình biên dịch Perl (dữ liệu được đọc từ hay ghi lên các tệp đều được xử lí tương tự, nhưng không đồng nhất).

Perl chấp nhận tập hợp đầy đủ các hằng kí hiệu dấu phẩy động có sẵn cho người lập trình C. Số có hay không có dấu chấm thập phân đều được phép (kể cả tiền tố cộng hay trừ tuỳ chọn), cũng như phần chỉ số mũ phụ thêm (kí pháp luỹ thừa) với cách viết E. Chẳng hạn:

1.25     # một và 1/4

7.25e45  # 7,25 x 1045 (một số dương lớn)

-6.5e24  # -6,5 x 1024 (một số âm lớn)

-12e-24  # -12 x 10-24 (một số âm rất nhỏ)

-1.2E-23 # tương tự như số ở trên: -1,2 x 10-23

2.2.3 Hằng kí hiệu nguyên

Các hằng kí hiện nguyên cũng được ghi trực tiếp, ví dụ:

12

15

-2004

3485

Bạn đừng bắt đầu một số bằng 0 (zero), vì Perl hỗ trợ cho hằng kí hiệu hệ cơ số tám và hệ mười sáu (hệt như kiểu C). Số hệ tám bắt đầu bằng số 0 đứng đầu, còn số hệ mười sáu thì bắt đầu bằng 0x hay 0X. Các chữ số hệ mười sáu từ A đến F (trong cả hai kiểu chữ hoa thường) đều biểu thị cho các giá trị số qui ước từ 10 đến 15. Chẳng hạn:

0377   # 377 hệ tám = 255 trong hệ thập phân

-0xff # âm FF hệ mười = -255 trong hệ thập phân

2.3 Xâu

Xâu là một dãy các kí tự (như Xin chào). Mỗi kí tự đều là một giá trị 8-bit trong toàn bộ tập 256 kí tự (không có gì đặc biệt về kí tự NULL như trong C).

Xâu ngắn nhất có thể được là xâu rỗng-không có kí tự nào. Xâu dài nhất thì chiếm trọn bộ nhớ máy tính (mặc dầu bạn sẽ chẳng thể nào làm gì nhiều với nó cả). Điều này phù hợp với nguyên lí “không có giới” mà Perl cung cấp cho bạn. Các xâu điển hình là các dẫy in được gồm các chữ, số và dấu ngắt trong phạm vi ASCII 32 tới ASCII 126. Tuy nhiên, khả năng để có bất kì kí tự nào từ 0 tới 255 trong một xâu có nghĩa là bạn có thể tạo ra, ‘nhòm’ qua, và thao tác dữ liệu nhị phân thô như các xâu – một cái gì đó mà phần lớn các trình tiện ích UNIX khác sẽ gặp khó khăn lớn. (chẳng hạn, bạn có thể ‘vá lỗi’ UNIX bằng việc đọc nó vào trong xâu Perl, tiến hành thay đổi, và ghi lại kết quả).

Giống như số, xâu có thể biểu diễn hằng kí hiệu (cách thức bạn biểu diễn xâu trong chương trình Perl). Các xâu hằng kí hiệu có theo hai hướng: xâu nháy đơn và xâu nháy kép.

2.3.1 Xâu dấu nháy đơn

Xâu dấu nháy đơn là một dẫy các kí tự được bao trong dấu nháy đơn (‘). Dấu nháy đơn không phải là một phần của bản thân xâu – chúng chỉ có đó để Perl xác định chỗ bắt đầu và kết thúc của xâu. Bất kì kí tự nào nằm giữa các dấu nháy (kể cả dấu xuống dòng, nếu xâu vẫn còn tiếp tục sang dòng sau) đều là hợp pháp bên trong xâu. Hai ngoại lệ: để lấy được một dấu nháy đơn trong một xâu có nháy đơn, bạn hãy đặt trước nó một dấu sổ chéo ngược. Và để lấy được dấu sổ chéo ngược trong một xâu có nháy đơn, bạn hãy đặt trước dấu sổ chéo ngược nột dấu sổ chéo ngược nữa. Ví dụ:

‘hello’     #năm kí tự: h, e, l, l, o

‘dont\’t’   #năm kí tự: d, o, n, nháy đơn, t

”          #xâu rỗng (không có kí tự)

‘silly\\me’ #silly, theo sau là một sổ chéo ngược, sau là me

“hello\n’   #hello theo sau là sổ chéo ngược và n

‘hello

there’      #hello, xuống dòng, there (toàn bộ 11 kí tự)

Chú ý rằng \n bên trong môt xâu có nháy đơn thì không được hiểu là dòng mới, nhưng nếu là hai kí tự sổ chéo ngược thì khác (chỉ khi sổ chéo ngược đi theo sau bởi một sổ chéo ngược khác hay một dấu nháy đơn thì mới mang nghĩa đặc biệt).

2.3.2 Xâu dấu nháy kép

Xâu dấu nháy kép hành động hệt như xâu trong C. Một lần nữa, nó lại là dãy các kí tự, mặc dầu lần này được bao bởi dấu ngoặc kép. Nhưng bây giờ dấu sổ chéo ngược lấy toàn bộ sức mạnh của nó để xác định các kí tự điều khiển nào đó, hay thậm chí bất kì kí tự nào qua các biểu diễn hệ tám hay hệ mười sáu. Đây là một số xâu dấu nháy kép:

“hello world\n” # hello world, và dấu xuống dòng

“new \177”      # new, dấu cách và kí tự xoá (177 hệ tám)

“coke\tsprite”  # coke, dấu tab, và sprite

Dấu sổ chéo có thể đứng trước nhiều kí tự khác nhau để hàm ý những điều khác nhau (về điển hình nó được gọi là lối thoát sổ chéo). Danh sách đầy đủ của các lối thoát xâu nháy kép được cho trong Bảng 2-1.

Bảng 2-1 Dấu sổ chéo ngược trong xâu nháy kép

Kết cấu Ý nghĩa

\n xuống dòng mới

\r về đầu dòng

\t ký tự tab

\f báo hết trang

\b xoá lui 1 ký tự

\a phát 1 tiếng “bíp”

\e escape

07 ký tự ASCII ở hệ tám (007=bíp)

x7f ký tự ASCII ở hệ mười sáu (7f=delete)

\cC ký tự điều khiển (ở đây là Ctrl-C)

\\ dấu sổ chéo ngược

\” dấu nháy kép

\l ký tự tiếp theo sẽ chuyển thành chữ thường

\L tất cả các ký tự tiếp theo cho tới \E sẽ thành chữ thường

\u ký tự tiếp theo sẽ chuyển thành chữ hoa

\U tất cả các ký tự tiếp theo cho tới \E sẽ thành chữ hoa

\E kết thúc \L hay \U

Một tính năng khác của xâu nháy kép là ở chỗ chúng cho phép chen lẫn các biến, nghĩa là một số tên biến nào đó bên trong xâu được thay thế bởi giá trị hiện tại của chúng khi xâu được dùng. Chúng ta đã không được giới thiệu một cách chính thức là các biến trông như thế nào (ngoại trừ trong phần mở đầu trên), cho nên tôi sẽ quay lại vấn đề này sau.

2.4 Toán tử

Một toán tử tạo ra một giá trị mới (kết quả) từ một hay nhiều giá trị khác (các toán hạng). Chẳng hạn, + là một toán tử vì nó nhận hai số (toán hạng, như 5 và 6), và tạo ra một giá trị mới (11, kết quả).

Các toán tử và biểu thức của Perl nói chung đều là siêu tập của các toán tử đã có trong hầu hết các ngôn ngữ lập trình tựa ALGOL/Pascal, như C. Một toán tử bao giờ cũng trông đợi các toán hạng số hay xâu (hay có thể là tổ hợp của cả hai). Nếu bạn cung cấp một toán hạng xâu ở chỗ đang cần tới một số, hay ngược lại, thì Perl sẽ tự động chuyển toán hạng đó bằng việc dùng các qui tắc khá trực giác, mà sẽ được nêu chi tiết trong mục “Chuyển đổi giữa số và xâu” dưới đây.

2.4.1 Toán tử cho số

Perl cung cấp các toán tử cộng, trừ, nhân, chia…điển hình thông thường. Chẳng hạn:

2 + 3      # 2 cộng 3, = 5

5.1 – 2.4  # 5.1 trừ 2.4, = 2.7

3 * 12     # 3 lần 12, = 36

14 / 2     # 14 chia cho 2, = 7

10.2 / 0.3 # 10.2 chia cho 0.3, = 34

10 / 3     # 10 chia cho 3, bao giờ cũng là phép chia dấu phẩy động, nên = 3.333…

Bên cạnh đó, Perl cung cấp toán tử lũy thừa kiểu FORTRAN, mà nhiều người đã từng mong mỏi cho Pascal và C. Toán tử này được biểu diễn bằng hai dấu sao, như 2**3 = 23 = 8. (nếu kết quả không thể khớp trong số dấu phẩy động độ chính xác gấp đôi, như một số âm mà lại luỹ thừa theo số không nguyên, hay một số lớn lấy luỹ thừa theo số lớn, thì bạn sẽ nhận được lỗi ‘định mệnh’ – fatal error).

Perl cũng hỗ trợ cho toán tử lấy đồng dư mô-đun như trong C. Giá trị của biểu thức 10 % 3 là số dư khi lấy 10 chia cho 3, chính là 1. Cả hai giá trị trước khi tính toán đều được đưa về giá trị nguyên, cho nên 10.5 % 3.2 được tính là 10 % 3.

Các toán tử so sánh logic là hệt như các toán tử có trong C (< <= == >= > !=), và việc so sánh hai giá trị về mặt số sẽ cho lại một giá trị đúng (True) hay sai (False). Chẳng hạn, 3 > 2 trả về True vì 3 lớn hơn 2, trong khi 5 != 5 trả về False. Các định nghĩa về đúng và sai được nói tới về sau, nhưng với hiện tại, các bạn hãy nghĩ về giá trị cho lại giống như chúng ở trong C – 1 là True, còn 0 là False. (các toán tử này sẽ được nói lại trong Bảng 2-2).

2.4.2 Toán tử xâu

Các giá trị xâu có thể được ghép với toán tử chấm (.) (đó là dấu chấm đơn). Điều này không làm thay đổi xâu, cũng như 2+3 không làm thay đổi 2 hay 3. Nhưng kết quả trả về là một xâu ghép (theo thứ tự) của 2 xâu thành phần, vậy là có sẵn cho tính toán thêm hay được cất giữ trong một biến:

“hello” . “world”     # tương tự như “helloworld”

‘hello wordl’ . “\n”  # hệt như “hello world\n”

“jerry” . ” ” . “tom” # hệt như “jerry tom”

Chú ý rằng việc ghép nối phải được gọi tường minh tới toán tử ., không giống awk là bạn đơn thuần chỉ việc đặt hai xâu gần nhau.

Một tập các toán tử cho xâu khác là toán tử so sánh xâu. Các toán tử này đều tựa FORTRAN, như lt thay cho bé hơn…Các toán tử so sánh các giá trị ASCII của các kí tự của xâu theo cách thông thường. Tập đầy đủ các toán tử so sánh (cho cả số và xâu) được nêu trong Bảng 2-2.

Bảng 2-2. Các toán tử so sánh số và xâu

Phép so sánh Số Xâu

Bằng == eq

Không bằng != ne

Bé hơn < lt

Lớn hơn > gt

Bé hơn hay bằng <= le

Lớn hơn hay bằng >= ge

Bạn có thể tự hỏi tại sao lại có các toán tử phân tách cho số và xâu vậy, nếu số và xâu được tự động chuyển đổi lẫn cho nhau? Ta hãy xét hai giá trị 7 và 30. Nếu được so sánh như số thì 7 hiển nhiên bé hơn 30, nhưng nếu được so sánh theo xâu, thì xâu “30” sẽ đứng trước xâu “7” (vì giá trị ASCII của 3 thì bé hơn giá trị ASCII của 7), và do đó là bé hơn. Cho nên, không giống awk, Perl đòi hỏi bạn xác định đúng kiểu so sánh, liệu đó là số hay xâu.

Chú ý rằng các phép so sánh số và xâu về đại thể ngược với những điều xẩy ra cho chỉ lệnh test của UNIX, mà thường dùng kí hiệu -eq để so sánh số còn = để so sánh xâu.

Vẫn còn một toán tử xâu khác là toán tử lặp lại xâu, bao gồm một kí tự chữ thường đơn giản x. Toán tử này lấy toán hạng trái của nó (một xâu), và thực hiện nhiều việc ghép bản sao của xâu đó theo số lần do toán hạng bên phải chỉ ra (một số). Chẳng hạn:

“jerry” x 3   # là “jerryjerryjerry”

“tom” x (4+1) # là “tom” x 5 hay “tomtomtomtomtom”

(3+2) x 4     # là 5 x 4, hay thực sự là “5” x 4, là “5555”

Nhưng toán tử lặp lại xâu cần một xâu cho toán hạng bên trái, cho nên số 5 được chuyển thành xâu “5” (dùng các qui tắc sẽ được mô tả chi tiết về sau), thành xâu một kí tự. Xâu mới này rồi được sao lên bốn lần, cho xâu bốn kí tự 5555. Chú ý rằng nếu ta đảo ngược trật tự các toán hạng, thì ta sẽ làm năm bản sao của xâu 4, cho 44444. Điều này chỉ ra rằng việc lặp lại xâu là không giao hoán.

Số đếm bản sao (toán hạng bên phải) trước hết sẽ bị chặt đi để cho giá trị nguyên (4.8 trở thành 4) trước khi được sử dụng. Số đếm bản sao bé hơn một sẽ gây ra kết quả là xâu rỗng (chiều dài không).

2.4.3 Thứ tự ưu tiên và luật kết hợp của toán tử

Thứ tự ưu tiên của toán tử xác định ra cách giải quyết trường hợp không rõ ràng khi nào dùng toán tử nào trên ba toán hạng. Chẳng hạn, trong biểu thức 2+3*4, ta sẽ thực hiện phép cộng trước hay phép nhân trước? Nếu ta làm phép cộng trước thì ta sẽ được 5*4, hay 20. Nhưng nếu ta làm phép nhân trước (như ta vẫn được dạ trong toán) thì ta được 2+12, hay 14. Perl chọn định nghĩa toán học thông thường, thực hiện nhân trước. Bởi điều này, ta nói nhân có số ưu tiên cao hơn cộng.

Bạn có thể phá đi trật tự theo số ưu tiên bằng việc dùng dấu ngoặc. Cho nên nếu tôi thực sự muốn cộng trước khi nhân, thì tôi có thể viết (2+3)*4, cho 20. Cũng vậy, nếu tôi muốn biểu thị rằng phép nhân được thực hiện trước phép cộng, tôi có thể trang điểm thêm, nhưng chẳng để làm gì, một cặp dấu ngoặc trong 2+(3*4).

Trong khi số ưu tiên là trực giác cho phép cộng và nhân thì ta bắt đầu lao vào vấn đề thường hay phải đương đầu với, chẳng hạn, phân biệt thế nào đối với phép ghép xâu và nâng lên luỹ thừa. Cách đúng đắn để giải quyết điều này là tra cứu sơ đồ số thứ tự ưu tiên toán tử của Perl, được nêu trong Bảng 2-3 (chú ý rằng một số các toán tử còn chưa được mô tả). Với những toán tử cũng có trong C, thì những toán tử đó có cùng số thứ tự ưu tiên như chúng có trong C (mà tôi chẳng bao giờ có thể nhớ được).

Bảng 2-3: Luật kết hợp và số ưu tiên của các toán tử (thấp nhất đến cao nhất)

Luật kết hợp Toán tử

Trái các toán tử về “danh sách” (mảng)

Trái –> (gọi hàm)

– (không xét) ++ (tăng 1) hoặc — (giảm 1)

Phải ** (luỹ thừa)

Phải ! ~ \ + – (phủ định lôgic, phủ định bit, tham khảo biến (như pointer trong C), số dương, số âm)

Trái =~ !~ (sánh, không sánh)

Trái * / % x (nhân, chia, đồng dư, lặp chuỗi)

Trái + – . (cộng, trừ, ghép xâu)

Trái << >> (dịch chuyển bit)

– các toán tử 1 toán hạng (như chomp)

– <  > <= >= lt gt le ge

– == != <=> eq ne cmp

Trái & (phép lôgíc ‘and’ theo bit)

Trái | ^ (phép lôgíc ‘or’, ‘xor’ theo bit)

Trái && (phép lôgíc ‘and’)

Left || (phép lôgíc ‘or’)

– ..(toán tử phạm vi), … (cấu tử danh sách)

Phải ?: (if-then-else, như trong C)

Phải = += -+ +=… (các phép gán)

Trái , => (ngăn cách bằng , và bằng =>)

– các toán tử về danh sách (array)

Phải not (phép lôgíc ‘not’)

Trái and (phép lôgíc ‘and’)

Trái or xor (phép lôgíc ‘or’, ‘xor’)

Trong sơ đồ này, bất kì toán tử đã cho nào đều có số ưu tiên lớn hơn các toán tử được liệt kê trên nó, và có số ưu tiên thấp hơn các toán tử được liệt kê dưới nó. Các toán tử tại cùng mức ưu tiên được giải quyết theo luật kết hợp.

Giống như với số ưu tiên, luật kết hợp giải quyết trật tự của các phép toán khi hai toán tử có cùng mức ưu tiên cùng tác động trên ba toán hạng:

2 ** 3 ** 4 # 2 ** (3 ** 4), hay 2 ** 81, hay xấp xỉ 2.41e24

72 / 12 / 3 # (72 / 12) / 3, hay 6 / 3, hay 2

30 / 6 * 3  # (30/6)*3, hay 15

Trong trường hợp thứ nhất, toán tử ** có luật kết hợp phải, cho nên các dấu ngoặc được áp dụng từ bên phải. So sánh với nó, các toán tử * và / có luật kết hợp trái, cho tập các dấu ngoặc bên trái.

2.4.4 Chuyển đổi giữa số và xâu

Nếu một giá trị xâu được dùng như một toán hạng cho một toán tử số (chẳng hạn, +), thì Perl sẽ tự động chuyển xâu thành giá trị số tương đương, dường như nó đã được đưa vào như một giá trị dấu phẩy động. Những chất liệu phi số đằng đuôi và khoảng trắng đằng đầu đều bị bỏ qua một cách yên lặng và lễ phép, cho nên ” 123.45fred” (với dấu cách đứng trước) chuyển thành 123.45 với lời ‘nhắc nhở nhẹ nhàng’. Tại một cực điểm của điều này, một cái gì đó không phải là số tẹo nào sẽ được chuyển thành 0 mà không có báo trước (như xâu “jerry” được dùng như số).

Giống vậy, nếu một giá trị số được cho khi đang cần tới một giá trị xâu (cho phép ghép xâu chẳng hạn), thì giá trị số sẽ được mở rộng thành bất kì xâu nào sẽ được in ra cho số đó. Chẳng hạn, nếu bạn muốn ghép nối x và theo sau là kết quả của 4 nhân với 5 thì bạn có thể làm đơn giản là: “X”.(4*5) sẽ thành “X”.20, hay “X20″. (nhớ rằng các dấu ngoặc này buộc 4*5 phải được tính trước khi xem xét toán tử ghép nối xâu).

Nói cách khác, bạn không thực sự phải lo lắng gì về liệu bạn có một số hay một xâu (phần lớn thời gian). Perl thực hiện mọi chuyển đổi cho bạn.

2.5 Biến vô hướng

Một biến là một tên gọi cho một chỗ chứa giữ được một hay nhiều giá trị. Tên của biến là không đổi trong toàn bộ chương trình, nhưng giá trị hay các giá trị được chứa trong biến đó về cơ bản thì lại thay đổi đi thay đổi lại trong suốt sự thực hiện chương trình.

Một biến vô hướng thì giữ một giá trị vô hướng riêng (biểu thị cho một số, hay một xâu, hay cả hai). Các tên biến vô hướng bắt đầu với dấu $ và tiếp theo sau là một chữ, rồi thì có thể là nhiều chữ, số hay dấu gạch dưới. Chữ hoa và chữ thường là phân biệt: biến $A là khác biến $a. Và tất cả các chữ, số và gạch thấp đều có nghĩa, cho nên: $a_very_long_variable_that_ends_in_1 là khác với $a_very_long_variable_that_ends_in_2.

Bạn nói chung nên chọn tên biến mang một nghĩa nào đó có liên quan tới giá trị của biến đó. Chẳng hạn, $xyz123 có lẽ không mang tính mô tả nhiều lắm nhưng $line_length thì lại có nghĩa.

2.6 Các toán tử trên biến vô hướng

Phép toán thông dụng nhất trên biến vô hướng là phép gán, chính là cách đặt một giá trị cho một biến. Toán tử gán của Perl là dấu bằng (giống như C hay FORTRAN), để tên biến bên vế trái và cho giá trị của biểu thức bên vế phải, kiểu như:

$a = 17;     # cho $a giá trị 17

$b = $a + 3; # cho $b giá trị hiện tại của $a cộng với 3 (20)

$b = $b * 2; # cho $b giá trị của $b được nhân với 2 (40)

Chú ý rằng dòng cuối dùng biến $b hai lần: khi lấy được giá trị của nó (ở vế phải dấu =), và khi xác định xem phải đặt biểu thức tính được vào đâu (ở vế trái của dấu =). Điều này là hợp lệ, an toàn và trong thực tế, khá thông dụng. Trong thực tế, nó thông dụng đến mức chúng ta sẽ thấy trong vài phút đây là ta có thể viết điều này bằng việc dùng cách viết tắt qui ước.

Bạn có thể đã chú ý rằng các biến vô hướng bao giờ cũng được tham khảo bằng dấu $ đứng trước. Trong các kịch bản shell, bạn dùng $ để lấy một giá trị, nhưng để $ đứng một mình để gán một giá trị mới. Trong awk hay C, bạn để cho $ đứng riêng hoàn toàn.

Việc gán vô hướng có thể được dùng như một giá trị cũng như một phép toán, như trong C. Nói cách khác, $a = 3 có một giá trị, cũng như $a+3 có một giá trị. Giá trị chính là số được gán, cho nên giá trị của $a = 3 là 3. Mặc dầu điều này dường như có vẻ kì lạ lúc thoáng nhìn, việc dùng một phép gán như một giá trị lại có ích nếu bạn muốn gán một giá trị trung gian trong một biểu thức cho một biến, hay nếu bạn muốn đơn giản sao cùng một giá trị cho một hay nhiều biến. Chẳng hạn:

$b = 4 + ($a = 3); # gán 3 cho $a, rồi cộng kết quả đó với 4 đặt vào $b, được 7

$d = ($c = 5);     # sao 5 vào $c, và rồi sao vào $d

$d = $c = 5;       # tương tự như trên, nhưng không có dấu ngoặc

2.6.1 Toán tử gán hai ngôi

Gần như tất cả các toán tử hai ngôi tính một giá trị đều có dạng phép gán hai ngôi tương ứng với dấu bằng có bổ sung thêm phần tử. Chẳng hạn, hai dòng sau đây là tương đương:

$a = $a + 5; # không có toán tử gán hai ngôi

$a += 5;     # có toán tử gán hai ngôi

Và tương tự như thế:

$b = $b * 3;

$b *= 3;

Trong từng trường hợp, toán tử này làm cho giá trị hiện tại của biến được thay đổi theo một cách nào đó, thay vì đơn giản ghi đè lên giá trị này bằng kết quả của một biểu thức mới nào đó. Toán tử gán thông dụng khác là toán tử ghép nối xâu:

$str = $str . ” “; # thêm dấu cách vào $str

$str .= ” “;       # cũng điều ấy với toán tử gán

Gần như tất cả các toán tử hai ngôi đều hợp lệ theo cách này. Chẳng hạn, toán tử nâng lên luỹ thừa của sẽ được viết là **=. Cho nên, $a **= 3 có nghĩa là “nâng một số trong $a lên luỹ thừa ba, rồi đặt kết quả trở lại $a”. Giống như toán tử gán đơn, các toán tử này cũng có một giá trị: giá trị mới của biến. Chẳng hạn:

$a = 3;

$b = ($a += 4); # $a và $b cả hai bây giờ đều là 7

Nhưng không may là trật tự tính toán của các toán hạng của toán tử hai ngôi lại không được xác định, cho nên một số biểu thức không thể nào được xác định hoàn toàn:

$a = 3;

$b = ($a += 2) * ($a -= 2); # Chương trình tồi: $b có thể là 15 hay 3

Nếu toán hạng bên phải của phép nhân được tính đầu tiên thì kết quả sẽ là 3 lần 1, hay 3. Tuy nhiên, nếu toán hạng bên trái được tính trước toán hạng bên phải, thì nó là 5 lần 3, hay 15.

2.6.2 Tự tăng và tự giảm

Dường như cũng đã đủ dễ dàng để thêm 1 vào $a bằng việc nói $a += 1. Perl còn đi xa hơn và thậm chí lại còn làm ngắn hơn cho điều này nữa. Toán tử ++ (được gọi là toán tử tự tăng) cộng thêm một vào toán hạng của nó, và cho lại giá trị đã được tăng, giống như:

$a += 1;   # dùng toán tử gán

++$a;      # dùng tự tăng tiền tố

$d = 17;

$e = ++$d; # $e và $d bây giờ đều là 18

Tại đây, toán tử ++ được dùng như toán tử tiền tố; tức là, toán tử xuất hiện ở bên trái toán hạng của nó. Phép tự tăng cũng có thể được dùng trong dạng hậu tố (nằm ở bên phải toán hạng của nó). Trong trường hợp này, kết quả của biểu thức này là giá trị của biến trước khi biến được tăng lên. Chẳng hạn:

$c = 17;

$d = $c++; # $d là 17, nhưng $c bây giờ là 18

Vì giá trị của toán hạng thay đổi nên toán hạng này phải là một biến vô hướng, không phải là biểu thức. Bạn không thể nói ++16 để có được 17, mà cũng không thể nói ++($a+$b) (là cách nào đó để có được giá trị lớn hơn tổng của $a và $b một đơn vị).

Toán tử tự giảm (–) cũng tương tự như toán tử tự tăng, nhưng trừ đi một thay vì cộng với một. Giống như toán tử tự tăng, toán tử tự giảm cũng có dạng tiền tố và hậu tố. Chẳng hạn:

$x = 12;

–$x;      # $x bây giờ là 11

$y = $x–; # $y là 11, còn $x bây giờ là 10

Không giống C, các toán tử tự tăng và tự giảm làm việc trên số dấu phẩy động. Cho nên việc tăng một biến với giá trị 4.2 sẽ cho 5.2 như dự kiến.

2.6.3 Toán tử chop() và chomp()

Một toán tử khác là chop(). Toán tử tiền tố này nhận một đối bên trong các dấu ngoặc của nó – tên của một biến vô hướng – và bỏ đi kí tự cuối cùng từ giá trị xâu của biến đó. Chẳng hạn:

$x = “Xin chào mọi người”;

chop($x);                   # $x bây giờ là “Xin chào mọi ngườ”

Lưu ý rằng giá trị của đối bị thay đổi ở đây, do đó cần phải có một biến vô hướng, thay vì chỉ đơn giản là giá trị vô hướng. Sẽ là vô nghĩa, chẳng hạn, để viết chop(‘suey’) để biến nó thành ‘sue’’, vì không có chỗ nào để cất giữ giá trị này. Bên cạnh đó, bạn có thể chỉ viết ‘sue’ cũng đủ.

Toán tử này trông giống như một lời gọi hàm, và quả thực trả về một giá trị (mà bạn sẽ trông đợi nếu bạn quen thuộc với lời gọi hàm từ các ngôn ngữ). Giá trị được cho lại chính là kí tự đã bị loại bỏ (chữ i trong người ở trên). Điều này có nghĩa là đoạn mã sau đây có lẽ sai:

$x = chop($x); # SAI: thay thế $x bằng ksi tự cuối cùng của nó

chop($x);      # Đúng: như trên, loại bỏ kí tự cuối

Nếu chop() được cho một xâu rỗng, thì nó chẳng làm gì cả.

chomp() tương tự như chop() nhưng nó chỉ xoá nếu ký tự cuối dùng của biến là ký tự dòng mới mà thôi, nếu là ký tự khác thì nó không làm gì cả. Bạn nên dùng chomp() thay cho chop() khi có thể vì nó an toàn hơn.

2.6.4 Xen lẫn vô hướng vào trong xâu

Khi một hằng kí tự xâu là được đặt trong nháy kép thì nó là chủ đề cho việc xen lẫn biến (bên cạnh việc được kiểm tra cho lối thoát sổ chéo ngược). Điều này có nghĩa là xâu này được duyệt qua để tìm các tên biến vô hướng có thể – có nghĩa là dấu $ đi theo sau một chữ, số hay dấu gạch dưới. Khi tìm thấy một tham khảo biến thì nó được thay thế bằng giá trị hiện tại (hay bất kì xâu rỗng nào nếu biến vô hướng còn chưa được gán giá trị nào). Chẳng hạn:

$a = “jerry”;

$b = “some text $a”;           # $b bây giờ là “some text jerry”

$c = “no such variable $what”; # $c là “no such variable ”

Để ngăn việc thay thế một biến bằng giá trị của nó, bạn phải hoặc làm thay đổi phần đó của xâu để cho nó xuất hiện trong ngoặc đơn, hoặc đặt trước dấu $ một dấu sổ chéo ngược:

$jerry = ‘hi’;

$tom = “a test of “.’$jerry’; # hằng kí hiệu: ‘a test of $jerry’

$tom2 = “a test of \$jerry”;  # cũng như vậy

Tên biến sẽ là tên biến dài nhất có thể mà tạo nên nghĩa tại phần đó của xâu. Điều này có thể là vấn đề nếu bạn muốn đặt sau ngay giá trị được thay thế với một văn bản hằng mà bắt đầu bằng một chữ, số hay dấu gạch thấp. Vì Perl duyệt qua các tên biến nên nó sẽ xét những kí tự là các kí tự tên phụ, mà không phải là điều bạn muốn. Perl cung cấp một định biên cho tên biến theo các hệ thống tương tự như lớp vỏ. Bạn hãy đơn thuần bao tên của biến đó trong một cặp dấu { và }. Hay bạn có thể kết thúc phần đó của xâu và bắt đầu một phần khác của xâu bằng toán tử ghép nối:

$fred = ‘pay’; $fredday = “wrong!”;

$tom = “It’s $fredday”;               # không phải payday, mà là “It’s wrong!”

$tom = “It’s ${fred}day”;             # bây giờ, $tom là “It’s payday!”

$barney2 = “It’s $fred”.’day’;        # cách khác để làm việc đó

$barney3 = “It’s ” . $fred . “day”;   # và một cách khác nữa

Toán tử sổ chéo ngược chuyển hoa thường có thể được dùng để làm thay đổi chữ hoa thường được đem theo cùng việc xen lẫn biến. Chẳng hạn:

$bigfred = “\Ufred”;                                  # $bigfred là FRED

$jerry = “fred”; $bigfred = “\U$jerry”;               # tương tự như trên

$capfred = “\u$jerry”;                                # $capfred là “Fred”

$tom = “\LBARNEY”;                                    # $tom bây giờ là “barney”

$capbarney = “\u\LBARNEY”;                            # capbarney bây giờ là “Barney”

$bigbarney = “BARNEY”; $capbarney = “\u\L$bigbarney”; # như trên

Như bạn có thể thấy, các toán tử dịch chuyển hoa thường được ghi nhớ bên trong xâu chừng nào chúng còn chưa được dùng tới, cho nên ngay kí tự đầu tiên của $capbarney không tuân theo \u, nó vẫn còn là chữ hoa vì \u chưa được dùng đến cho đến khi \L được dùng xong.

Thuật ngữ xen lẫn biến thường được dùng lẫn với xen lẫn nháy kép, vì các xâu có nháy kép là chủ đề cho việc xen lẫn biến.

2.7 <STDIN> xem như một vô hướng

Tại điểm này, bạn có thể tự hỏi làm sao lấy được một giá trị vào trong chương trình Perl. Sau đây là cách đơn giản nhất. Mỗi lần bạn dùng <STDIN> ở chỗ đang trông đợi một giá trị vô hướng, thì Perl sẽ đọc toàn bộ dòng văn bản tiếp từ lối vào chuẩn (cho tới dấu dòng mới đầu tiên), và dùng xâu đó như giá trị cho <STDIN>. Đầu vào chuẩn có thể mang nhiều nghĩa, nhưng chừng nào bạn còn chưa làm điều gì đó thì nó vẫn còn mang nghĩa là thiết bị cuối của người dùng, người đã gọi chương trình của bạn (có thể là bạn). Nếu không có gì chờ đợi để đọc cả (trường hợp điển hình, chừng nào bạn còn chưa gõ xong toàn bộ dòng), thì chương trình Perl sẽ dừng và đợi cho bạn đưa vào một số kí tự theo sau bằng một dấu dòng mới (xuống dòng).

Giá trị xâu của <STDIN> về điển hình có một dấu dòng mới ở cuối của nó. Thông thường nhất là bạn muốn gỡ bỏ cái dấu dòng mới đó đi (có sự khác biệt lớn giữa hello và hello\n) (dùng toán tử chop() để hỗ trợ). Một dãy data input đại loại như thế này:

$a = <STDIN>; # nhận văn bản

chomp($a);    # gỡ bỏ dấu dòng mới

Cách viết tắt thông dụng cho hai dòng này là:

chomp($a = <STDIN>);

Phép gán bên trong các dấu ngoặc tròn tiếp tục là một tham khảo tới $a, thậm chí sau khi nó đã được trao cho một giá trị với toán tử <STDIN>. Vậy, toán tử chomp() làm việc trên $a. (Điều này là đúng nói chung đối với toán tử gán – một biểu thức gán có thể được dùng bất kì khi nào một biến là cần tới, và những hành động tham khảo tới biến đó ở bên trái của dấu bằng).

2.8 Đưa ra bằng print()

Vậy ta thu được mọi thứ với <STDIN>. Ta làm sao đưa ra mọi thứ đây? Bằng toán tử print(). Toán tử tiền tố này nhận một giá trị vô hướng bên trong các dấu ngoặc của nó và đưa ra mà không cần bất kì sự trang điểm nào lên lối ra chuẩn. Một lần nữa, chừng nào bạn còn chưa làm điều gì thì lối ra này vẫn cứ là thiết bị cuối của bạn. Chẳng hạn:

print(“Xin chào mọi người\n”); # nói chào mọi người, tiếp là dấu dòng mới

print “Xin chào mọi người\n”;  # tương tự như trên

Lưu ý rằng thí dụ thứ hai chỉ ra dạng của print() không có dấu ngoặc. Trong thực tế, nhiều toán tử trông như các hàm cũng có dạng cú pháp làm việc không cần dấu ngoặc. Dù có dùng hay không, dấu ngoặc cũng gần như là vấn đề về kiểu cách và sự nhanh nhẩu trong cách gõ, mặc dầu có vài trường hợp bạn sẽ cần các dấu ngoặc để loại bỏ bớt sự mập mờ.

Chúng ta sẽ thấy rằng bạn thực tế có thể cho print một danh sách các giá trị, trong mục “Dùng print để xuất” ở Chương 6: Nhấp/Xuất căn bản, nhưng chúng ta vẫn còn chưa nói về danh sách, cho nên chúng ta sẽ nói nó về sau.

2.9 Giá trị undef

Điều gì sẽ xảy ra nếu bạn dùng một biến vô hướng trước khi bạn cho nó một giá trị? Chẳng có gì nghiêm trọng cả. Các biến đều có giá trị undef trước khi chúng được gán lần đầu tiên. Giá trị này trông như số 0 khi được dùng như một số, hay xâu rỗng chiều dài 0 nếu được dùng như một xâu. Nhiều toán tử trả về undef khi các đối vượt ra ngoài phạm vi và thành vô nghĩa.

Một toán tử mà chúng ta đã thấy có trả về undef là toán tử <STDIN>. Thông thường toán tử này cho lại một xâu của dòng tiếp vừa được đọc, tuy nhiên (như khi bạn gõ control-D tại thiết bị cuối, hay khi một tệp không còn dữ liệu nữa), thì toán tử này trả vềi undef như một giá trị. Chúng ta sẽ thấy trong chương 6 cách kiểm tra này và chọn hành động đặc biệt khi không còn dữ liệu nào có sẵn để đọc nữa.

2.10 Bài tập

Bạn hãy viết 1 chương trình tính và in ra chu vi của 1 đường tròn cho bán kính là 12.5.

Hãy sửa lại chương trình ở trên sao cho chương trình đưa ra 1 câu thông báo và đợi người dùng nhập bán kính vào từ bàn phím.

Bạn hãy viết chương trình đọc vào 2 số và in ra tích của 2 số đó.

Bạn hãy viết chương trình đọc vào một xâu và một số, sau đó in ra xâu đó với số lần được chỉ định bởi giá trị của số được nhập vào (dùng toán tử x)

(Sưu tầm từ diễn đàn tin học)


Thiết kế bởi Phạm Nguyễn Bảo Nguyên
GC Com 2005

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: