Tải bản đầy đủ (.pdf) (143 trang)

Lịch sử của perl

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.05 MB, 143 trang )

Lời giới thiệu
Cuốn sách này nói về gì
1: Giới thiệu
Lịch sử của Perl
Mục đích của Perl
Những điều có sẵn
Hỗ trợ
Các khái niệm cơ bản
Dạo qua Perl
Bài tập
2:Dữ liệu vô hướng
Dữ liệu vô hướng là gì?
Số
Xâu
Toán tử
Biến vô hướng
Toán tử trên biến vô hướng
<STDIN> như giá trị vô hướng
Đưa ra với print()
Giá trị undef
Bài tập
3: Dữ liệu mảng và danh sách
Mảng là gì?
Biểu diễn hằng kí hiệu
Biến
Toán tử
Vô hướng và ngữ cảnh mảng
<STDIN> như một mảng
Nội suy biến thiên của mảng
Bài tập
4: Cấu trúc điều khiển


Khối câu lệnh
Câu lệnh if/unless
Câu lệnh while/until
Câu lệnh for
Câu lệnh foreach
Bài tập
5: Mảng kết hợp
Mảng kết hợp là gì?


Biến mảng kết hợp
Biểu diễn hằng kí hiệu cho mảng kết hợp
Toán tử mảng kết hợp
Bài tập
6: Vào/ra cơ sở
Đưa vào từ STDIN
Đưa vào từ toán tử hình thoi
Đưa ra STDOUT
Bài tập
7: Biểu thức chính qui
Khái niệm về biểu thức chính qui
Cách dùng đơn giản về biểu thức chính qui
Mẫu
Nói thêm về toán tử đối sánh
Phép thế
Các toán tử split(0 và join()
Bài tập
8: Hàm
Các hàm hệ thống và người dùng
Định nghĩa hàm người dùng

Gọi hàm người dùng
Cho lại giá trị
Đối
Biến cục bộ trong hàm
Bài tập
9: Các cấu trúc điều khiển
Toán tử last
Toán tử next
Toán tử redo
Khối có nhãn
Bộ sửa đổi biểu thức
&&, || và ?: xem như các cấu trúc điều khiển
Bài tập
10: Giải quyết tệp và kiểm thử tệp
Tước hiệu tệp là gì?
Mở và đóng một tước hiệu tệp
Một chút tiêu khiển: die()
Dùng tước hiệu tệp
Kiểm tra tệp -x
Toán tử stat() và lstat()


Dùng _Filehandle
Bài tập
11: Định dạng
Định dạng là gì?
Xác định một định dạng
Gọi một định dạng
Nói thêm về Fieldholder
Định dạng đỉnh trang

Đổi giá trị mặc định cho định dạng
Bài tập
12: Thâm nhập danh mục
Đi vòng quanh cây danh mục
Globbing
Giải quyết danh mục
Mở và đóng bộ giải quyết danh mục
Đọc bộ giải quyết danh mục
Bài tập
13: Thao tác tệp và danh mục
Loại bỏ tệp
Đổi tên tệp
Tạo ra tên thay phiên cho một tệp (liên kết)
Tạo ra và xoá danh mục
Sửa đổi phép dùng
Sửa đổi quyền dùng
Sửa đổi thời hạn
Bài tập
14: Quản lí tiến trình
Dùng system() và exec()
Dùng Backquotes
Dùng Proceses và Filehandles
Dùng folk
Tóm tắt về các phép toán tiến trình
Gửi và nhận tín hiệu
Bài tập
15: Biến đổi dữ liệu khác
Tìm xâu con
Trích và thay thế xâu con
Định dạng dữ liệu bằng sprintf()

Sắp xếp cao cấp
Chuyển tự


Bài tập
16: Thâm nhập cơ sở dữ liệu hệ thống
Lấy mật hiệu và thông tin nhóm
Đóng và mở gói dữ liệu nhị phân
Lấy thông tin mạng
Lấy thông tin khác
Bài tập
17: Thao tác cơ sở dữ liệu người dùng
Cơ sở dữ liệu DBM và mảng DBM
Mở và đóng mảng DBM
Dùng mảng DBM
Cơ sở dữ liệu thâm nhập ngẫu nhiên chiều dài cố định
Cơ sở dữ liệu chiều dài biến thiên (văn bản)
Bài tập
18: Chuyển các ngôn ngữ khác sang Perl
Chuyển Chương trình awk sang Perl
Chuyển Chương trình sed sang Perl
Bài tập
A: Trả lời bài tập
B: Cơ sở về nối mạng
Mô hình chỗ cắm
Khách mẫu
Bộ phục vụ mẫu
C: Những chủ đề chưa đề cập tới
Bộ gỡ lỗi
Dòng lệnh

Các toán tử khác
Nhiều, nhiều hàm nữa
Nhiều, nhiều biến đặt sẵn nữa
Xâu ở đây
return (từ chương trình con)
Toán tử eval (và s///e)
Thao tác bảng kí hiệu với *FRED
Toán tử goto
Toán tử require
Thư viện
Perl bản 5.0 Goodies
Chỉ mục



Trong chương này:
Lịch sử Perl
Mục đích của Perl
Có sẵn
Hỗ trợ
Các khái niệm cơ bản
Dạo qua về Perl

1

Giới thiệu

Lịch sử Perl
Perl là cách viết tắt cho “Practical Extraction and Report Language” Ngôn
ngữ báo cáo và trích rút thực hành, mặc dầu nó cũng còn được gọi là

“Pathologically Eclectic Rubbish Lister” - Bộ làm danh sách chiết trung bậy bạ
và bệnh hoạn. Chẳng ích gì mà biện minh xem cách gọi nào đúng hơn, vì cả hai
đều được Larry Wall, người sáng tạo và kiến trúc sư chính, người cài đặt và bảo
trì của Perl chấp nhận. Ông ấy đã tạo ra Perl khi cố gắng sản xuất ra một số báo
cáo từ một cấp bậc các tệp kiểu như thư người dùng mạng Usenet về hệ thống
báo lỗi, và lệnh awk làm xì hết hơi. Larry, một người lập trình lười biếng, quyết
định thanh toán vấn đề này bằng một công cụ vạn năng mà anh có thể dùng ít
nhất cũng ở một nơi khác. Kết quả là bản đầu tiên của Perl.
Say khi chơi với bản đầu này của Perl một chút, thêm chất liệu đây đó, Larry
đưa nó cho cộng đồng độc giả Usenet, thường vẫn được gọi là “the Net”. Người
dùng thuộc toán phù du nghèo khó về hệ thống trên toàn thế giới (quãng độ chục
nghìn người) đưa lại cho anh phản hồi, hỏi cách làm thế này thế kia, việc này
việc khác, nhiều điểm mà Larry chưa bao giờ mường tượng ra về việc giải quyết
cho Perl nhỏ bé của mình cả.
Nhưng kết quả là Perl trưởng thành, trưởng thành và trưởng thành thêm nữa,
và cũng cùng tỉ lệ như lõi của UNIX. (với bạn là người mới, toàn bộ lõi UNIX
được dùng chỉ khít vào trong 32K! Và bây giờ chúng ta may mắn nếu ta có thể
có được nó dưới một vài mega.) Nó đã trưởng thành trong các tính năng. Nó đã
trưởng thành trong tính khả chuyển. Điều mà có thời là một ngôn ngữ tí tẹo bây
giờ đã có tài liệu sử dụng 80 trang, một cuốn sách của Nutshell 400 trang, một
nhóm tin Usenet với 40 nghìn thuê bao, và bây giờ là đoạn giới thiệu nhẹ nhàng
này.
Larry vẫn là người bảo trì duy nhất, làm việc trên Perl ngoài giờ khi kết thúc
công việc thường ngày của mình. Và Perl thì vẫn phát triển.
Một cách đại thể thì lúc mà cuốn sách này đạt tới điểm dừng của nó, Larry
sẽ đưa ra bản Perl mới nhất, bản 5.0, hứa hẹn có một số tính năng thường hay
được yêu cầu, và được thiết kế lại từ bên trong trở ra. (Larry bảo tôi rằng không
còn mấy dòng lệnh từ lần đưa ra trước, và số ấy cứ ngày càng ít đi mỗi ngày.)
Tuy nhiên, cuốn sách này đã được thử với Perl bản 4.0 (lần đưa ra gần đây nhất



khi tôi viết điều này). Mọi thứ ở đây đều sẽ làm việc với bản 5.0 và các bản sau
của Perl. Trong thực tế, chương trình Perl 1.0 vẫn làm việc tốt với những bản
gần đây, ngoại trừ một vài thay đổi lạ cần cho sự tiến bộ.
Mục đích của Perl
Perl được thiết kế để trợ giúp cho người dùng UNIX với những nhiệm vụ
thông dụng mà có thể rất nặng nề hay quá nhậy cảm với tính khả chuyển đối với
trình vỏ, và cũng quá kì lạ hay ngắn ngủi hay phức tạp để lập trình trong C hay
một ngôn ngữ công cụ UNIX nào khác.
Một khi bạn trở nên quen thuộc với Perl, bạn có thể thấy mình mất ít thời
gian để lấy được trích dẫn trình vỏ (hay khai báo C) đúng, và nhiều thời gian
hơn để đọc tin trên Usenet và đi trượt tuyết trên đồi; vì Perl là một công cụ lớn
tựa như chiếc đòn bẩy. Các cấu trúc chặt chẽ của Perl cho phép bạn tạo ra (với
tối thiểu sự om sòm nhặng sị) một số giải pháp có ưu thế rất trần lặng hay những
công cụ tổng quát. Cũng vậy, bạn có thể lôi những công cụ này sang công việc
tiếp, vì Perl là khả chuyển cao độ và lại có sẵn, cho nên bạn sẽ có nhiều thời
gian hơn để đọc tin Usenet và trượt tuyết.
Giống như mọi ngôn ngữ, Perl có thể “chỉ viết” - tức là có thể viết ra chương
trình mà không thể nào đọc được. Nhưng với sự chú ý đúng đắn, bạn có thể
tránh được sự kết tội thông thường này. Quả thế, đôi khi Perl trông như nổi tiếng
với những cái không quen thuộc, nhưng với người lập trình đã thạo Perl, nó tựa
như những dòng có tổng kiểm tra với một sứ mệnh trong cuộc đời. Nếu bạn tuân
theo những hướng dẫn của cuốn sách này thì chương trình của bạn sẽ dễ đọc và
dễ bảo trì, và chúng có lẽ sẽ không thắng trong bất kì cuộc tranh luận Perl khó
hiểu nào.
Tính sẵn có
Nếu bạn nhận được
Perl: not found
khi bạn thử gọi Perl từ lớp vỏ thì người quản trị hệ thống của bạn cũng chẳng lên
cơn sốt. Nhưng thậm chí nếu nó không có trên hệ thống của bạn, thì bạn vẫn có

thể lấy được nó không mất tiền (theo nghĩa “ăn trưa không mất tiền”).
Perl được phân phối theo phép công cộng GNU, nghĩa là thế này, “bạn có
thể phân phát chương trình nhị phân Perl chỉ nếu bạn làm ra chương trình gốc có
sẵn cho mọi người dùng không phải trả tiền gì cả, và nếu bạn sửa đổi Perl, bạn
phải phân phát chương trình gốc của bạn cho nơi sửa đổi của bạn nữa.” Và đó là
bản chất của cho không. Bạn có thể lấy chương trình gốc của Perl với giá của
một băng trắng hay vài mêga byte qua đường dây. Và không ai có thể khoá Perl
và bán cho bạn chỉ mã nhị phân với ý tưởng đặc biệt về “cấu hình phần cứng
được hỗ trợ”.
Trong thực tế, nó không chỉ là cho không, nhưng nó chạy còn gọn hơn trên
gần như mọi thứ mà có thể gọi là UNIX hay tựa UNIX và có trình biên dịch C.


Đấy là vì bộ trình này tới với bản viết cấu hình bí quyết được gọi là Cấu hình,
cái sẽ móc và chọc vào các danh mục hệ thống để tìm những thứ nó cần, và điều
chỉnh việc đưa vào các tệp và các kí hiệu được xác định tương ứng, chuyển cho
bạn việc kiểm chứng phát hiện của nó.
Bên cạnh các hệ thống UNIX hay tựa UNIX, người đã bị nghiện Perl đem
nó sang Amiga, Atari ST, họ Macintosh, VMS, OS/2, thậm chí MS/DOS - và có
lẽ còn nhiều hơn nữa vào lúc bạn đọc cuốn sách này. Vị trí chính xác và sự có
sẵn của những bản Perl này thì biến động, cho nên bạn phải hỏi quanh (trên
nhóm tin Usenet chẳng hạn) để có được thông tin mới nhất. Nếu bạn hoàn toàn
không biết gì, thì một bản cũ của Perl đã có trên đĩa phần mềm CD-ROM UNIX
Power Tools, của Jerry Peek, Tim O’Reilly và Mike Loukides (O’Reilly &
Associates/ Random House Co., 1993).
Hỗ trợ
Perl là con đẻ của Larry Wall, và vẫn đang được anh ấy nâng niu. Báo cáo
lỗi và yêu cầu nâng cao nói chung đều được sửa chữa trong các lần đưa ra sau,
nhưng anh ấy cũng chẳng có nghĩa vụ nào để làm bất kì cái gì với chúng cả. Tuy
thế Larry thực sự thích thú nghe từ tất cả chúng ta, và cũng làm việc thực sự để

thấy Perl được dùng trên qui mô thế giới. E-mail trực tiếp cho anh ấy nói chung
đều nhận được trả lời (cho dù đấy chỉ đơn thuần là máy trả lời email của anh ấy),
và đôi khi là sự đáp ứng con người.
Ích lợi hơn việc viết thư trực tiếp cho Larry là nhóm hỗ trợ Perl trực tuyến
toàn thế giới, liên lạc thông qua nhóm tin Usenet comp.lang.perl. Nếu bạn có thể
gửi email trên Internet, nhưng chưa vào Usenet, thì bạn có thể tham gia nhóm
này bằng cách gửi một yêu cầu tới , yêu cầu sẽ
tới một người có thể nối bạn với cửa khẩu email hai chiều trong nhóm, và cho
bạn những hứong dẫn về cách làm việc.
Khi bạn tham gia một nhóm tin, bạn sẽ thấy đại loại có khoảng 30 đến 60
“thư” mỗi ngày (vào lúc bản viết này được soạn thảo) trên đủ mọi chủ đề từ câu
hỏi của người mới bắt đầu cho tới vấn đề chuyển chương trình phức tạp và vấn
đề giao diện, và thậm chí cả một hay hai chương trình khá lớn.
Nhóm tin gần như được những chuyên gia Perl điều phối. Phần lớn thời
gian, câu hỏi của bạn đều có sự trả lời trong vòng vài phút khi bài tin bạn tới tủ
nối Usenet chính. Bạn hãy thử mức độ hỗ trợ từ nhà sản xuất phần mềm mình ưa
chuộng về việc cho không này! Bản thân Larry cũng đọc về nhóm khi thời gian
cho phép, và đôi khi đã xen các bài viết có thẩm quyền vào để chấm dứt việc cãi
nhau hay làm sáng tỏ một vấn đề. Sau rốt, không có Usenet, có lẽ không thể có
chỗ để dễ dàng công bố Perl cho cả thế giới.
Bên cạnh nhóm tin, bạn cũng nên đọc tạp chí Perl, đi cùng việc phân phối
Perl. Một nguồn có thẩm quyền khác là cuốn sách Programming Perl của Larry
Wall và Randal L. Schwatrz (O’Reilly & Associaté, 1990). Programming Perl
được xem như “Sách con lừa” vì bìa của nó vẽ con vật này (hệt như cuốn sách
này có lẽ sẽ được biết tới với tên sách lạc đà không bướu). Sách con lừa chứa


thông tin tham khảo đầy đủ về Perl dưới dạng đóng gọn gàng. Sách con lừa cũng
bao gồm một bảng tra tham khảo bật ra tài tình mà chính là nguồn ưa chuộng
của cá nhân tôi về thông tin Perl.

Các khái niệm cơ bản
Một bản viết vỏ không gì khác hơn là một dãy các lệnh vỏ nhồi vào trong
một tệp văn bản. Tệp này “được làm cho chạy” bằng cách bật một bit thực hiện
(qua chmod +x filename) và rồi gõ tên của tệp đó vào lời nhắc của vỏ. Bingo,
một chương trình vỏ lớn. Chẳng hạn, một bản viết để chạy chỉ lệnh date theo sau
bởi chỉ lệnh who có thể được tạo ra và thực hiện như thế này:
$ echo date > somecript
$ echo who > somecript
$ cat somescript
date
who
$ chmod _x somescript
$ somescript
[output of date followed by who]
$
Tương tự như thế, một chương trình Perl là một bó các câu lệnh và định
nghĩa Perl được ném vào trong một tệp. Rồi bạn bật bit thực hiện và gõ tên của
tệp này tại lời nhắc của vỏ. Tuy nhiên, tệp này phải chỉ ra rằng đây là một
chương trình Perl và không phải là chương trình vỏ, nên chúng ta cần một bước
phụ.
#! /usr/bin/perl
làm dòng đầu tiên của tệp này. Nhưng nếu Perl của bạn bị kẹt vào một nơi
không chuẩn, hay hệ điều hành tựa UNIX của bạn không hiểu dòng #!, thì bạn
có thêm việc phải làm. Hãy hỏi người cài đặt Perl về điều này. Các thí dụ trong
sách này giả sử rằng bạn dùng cơ chế thông thường này.
Perl là một ngôn ngữ phi định dạng kiểu như C - khoảng trắng giữa các hiệu
bài (những phần tử của chương trình, như print hay +) là tuỳ chọn, trừ phi hai
hiệu bài đi với nhau có thể bị lầm lẫn thành một hiệu bài khác, trong trường hợp
đó thì khoảng trắng thuộc loại nào đó là bắt buộc. (Khoảng trắng bao gồm dấu
cách, dấu tab, dòng mới, về đầu dòng hay kéo giấy.) Có một vài cấu trúc đòi hỏi

một loại khoảng trắng nào đó ở chỗ nào đó, nhưng chúng sẽ được trỏ ra khi ta
nói tới chúng. Bạn có thể giả thiết rằng loại và khối lượng khoảng trắng giữa các
hiệu bài là tuỳ ý trong các trường hợp khác.
Mặc dầu gần như tất cả các chương trình Perl đều có thể được viết tất cả trên
một dòng, một cách điển hình chương trình Perl cũng hay được viết tụt lề như
chương trình C, với những phần câu lệnh lồng nhau được viết tụt vào hơn so với
phần bao quanh. Bạn sẽ thấy đầy những thí dụ chỉ ra phong cách viết tụt lề điển
hình trong toàn bộ cuốn sách này.


Cũng giống như bản viết về vỏ, chương trình Perl bao gồm tất cả các câu
lệnh perl về tệp được lấy tổ hợp chung như mọt trình lớn cần thực hiện. Không
có khái niệm về trình “chính” main như trong C.
Chú thích của Perl giống như chú thích của lớp vỏ (hiện đại). Bất kì cái gì
nằm giữa một dấu thăng (#) tới cuối dòng đều là một chú thích. Không có khái
niệm về chú thích trên nhiều dòng như C.
Không giống hầu hết các lớp vỏ (nhưng giống như awk và sed), bộ thông
dịch Perl phân tích và biên dịch hoàn toàn chương trình trước khi thực hiện nó.
Điều này có nghĩa là bạn không bao giờ nhận được lỗi cú pháp từ chương trình
một khi chương trình đã bắt đầu chạy, và cũng có nghĩa là khoảng trắng và chú
thích biến mất và sẽ không làm chậm chương trình. Trong thực tế, giai đoạn
biên dịch này bảo đảm việc thực hiện nhanh chóng của các thao tác Perl một khi
nó được bắt đầu, và nó cung cấp động cơ phụ để loại bỏ C như một ngôn ngữ
tiện ích hệ thống đơn thuần dựa trên nền tảng là C được biên dịch.
Việc biên dịch này không mất thời gian - sẽ là phi hiệu quả nếu một chương
trình Perl cực lớn lại chỉ thực hiện một nhiệm vụ nhỏ bé chóng vánh (trong số
nhiều nhiệm vụ tiềm năng) và rồi ra, vì thời gian chạy cho chương trình sẽ nhỏ
xíu nếu so với thời gian dịch.
Cho nên Perl giống như một bộ biên dịch và thông dịch. Nó là biên dịch vì
chương trình được đọc và phân tích hoàn toàn trước khi câu lệnh đầu tiên được

thực hiện. Nó là bộ thông dịch vì không có mã đích ngồi đâu đó trút đầy không
gian đĩa. Theo một cách nào đó, nó là tốt nhất cho cả hai loại này. Phải thú thực,
việc ẩn đi mã đích đã dịch giữa những lời gọi thì hay, và đó là trong danh sách
mong ước cho Perl tương lai của Larry.
Dạo qua Perl
Chúng ta bắt đầu cuộc hành trình của mình qua Perl bằng việc đi dạo một
chút. Việc đi dạo này sẽ giới thiệu một số các tính năng khác nhau bằng cách bổ
sung vào một ứng dụng nhỏ. Giải thích ở đây là cực kì ngắn gọn - mỗi vùng chủ
đề đều được thảo luận chi tiết hơn rất nhiều về sau trong cuốn sách này. Nhưng
cuộc đi dạo nhỏ này sẽ cho bạn kinh nghiệm nhanh chóng về ngôn ngữ, và bạn
có thể quyết định liệu bạn có thực sự muốn kết thúc cuốn sách này hay đọc thêm
các tin Usenet hay chạy đi chơi trượt tuyết.
Chương trình “Xin chào mọi người”
Ta hãy nhìn vào một chương trình nhỏ mà thực tế có làm điều gì đó. Đây là
chương trình “Xin chào mọi người”:
#!/usr/bin/perl
print “Xin chào mọi người\n”;
Dòng đầu tiên là câu thần chú nói rằng đây là chương trình Perl. Nó cũng là
lời chú thích cho Perl - hãy nhớ rằng lời chú thích là bất kì cái gì nằm sau dấu
thăng cho tới cuối dòng, giống như hầu hết các lớp vỏ hiện đại hay awk.


Dòng thứ hai là toàn bộ phần thực hiện được của chương trình này. Tại đây
chúng ta thấy câu lệnh print. Từ khoá print bắt đầu chương trình, và nó có một
đối, một xâu văn bản kiểu C. Bên trong xâu này, tổ hợp kí tự \n biểu thị cho kí
tự dòng mới; hệt như trong C. Câu lệnh print được kết thúc bởi dấu chấm phẩy
(;). Giống như C, tất cả các câu lệnh đơn giản đều kết thúc bằng chấm phẩy* .
Khi bạn gọi chương trình này, phần lõi sẽ gọi bộ thông dịch Perl, phân tích
câu toàn bộ chương trình (hai dòng, kể cả dòng chú thích đầu tiên) và rồi thực
hiện dạng đã dịch. Thao tác đầu tiên và duy nhất là thực hiện toán tử print, điều

này gửi đối của nó ra lối ra. Sau khi chương trình đã hoàn tất, thì tiến trình Perl
ra, cho lại một mã ra thành công cho lớp vỏ.
Hỏi câu hỏi và nhớ kết quả
Ta hãy thêm một chút phức tạp hơn. Từ Xin chào mọi người là một sự đụng
chạm lạnh nhạt và cứng rắn. Ta hãy làm cho chương trình gọi bạn theo tên bạn.
Để làm việc này, ta cần một chỗ giữ tên, một cách hỏi tên, và một cách nhận câu
trả lời.
Một loại đặt chỗ giữ giá trị (tựa như một tên) là biến vô hướng. Với chương
trình này, ta sẽ dùng biến vô hướng $name để giữ tên bạn. Chúng ta sẽ đi chi
tiết hơn trong Chương 2, Dữ liệu vô hướng, về những gì mà biến này có thể giữ,
và những gì bạn có thể làm với chúng. Hiện tại, giả sử rằng bạn có thể giữ một
số hay xâu (dãy các kí tự) trong biến vô hướng.
Chương trình này cần hỏi về tên. Để làm điều đó, ta cần một cách nhắc và
một cách nhận cái vào. Chương trình trước đã chỉ ra cho ta cách nhắc - dùng
toán tử print. Và cách để nhận một dòng từ thiết bị cuối là với toán tử
<STDIN>, mà (như ta sẽ dùng nó ở đây) lấy một dòng cái vào. Ta gán cái vào
này cho biến $name. Điều này cho ta chương trình:
print “Tên bạn là gì? : ”;
$name = <STDIN> ;
Giá trị của $name tại điểm này có một dấu dòng mới kết thúc (Randal có
trong Randal\n). Để vứt bỏ điều đó, chúng ta dùng toán tử chop(), toán tử lấy
một biến vô hướng làm đối duy nhất và bỏ đi kí tự cuối từ giá trị xâu của biến:
chop($name);
Bây giờ tất cả những gì ta cần làm là nói Xin chào, tiếp đó là giá trị của biến
$name, mà ta có thể thực hiện theo kiểu vỏ bằng cách nhúng biến này vào bên
trong xâu có ngoặc kép:
print “Xin chào, $name!\n”;
Giống như lớp vỏ, nếu ta muốn một dấu đô la thay vì tham khảo biến vô
hướng, thì ta có thể đặt trước dấu đô la với một dấu sổ chéo ngược.
Gắn tất cả lại, ta được:

*

Dấu chấm phẩy có thể bỏ đi khi câu lệnh này là câu lệnh cuối của một khối hay tệp hay eval.


#!/usr/bin/perl
print “Tên bạn là gì? ”;
$name = <STDIN> ;
chop($name);
print “Xin chào, $name!\n”;
Bổ sung chọn lựa
Bây giờ ta muốn có một lời chào đặc biệt cho Randal, nhưng muốn lời chào
thông thường cho mọi người khác. Để làm điều này, ta cần so sánh tên đã được
đưa vào với xâu Randal, và nếu hai xâu là một, thì làm điều gì đó đặc biệt. Ta
hãy bổ sung thêm lệnh rẽ nhánh if-then-else và phép so sánh vào chương trình:
#!/usr/bin/perl
print “Tên bạn là gì? ”;
$name = <STDIN> ;
chop($name);
if ($name eq “Randal”) {
print “Xin chào Randal! Tốt quá anh lại ở đây!\n”;
} else {
print “Xin chào, $name!\n”; # chào mừng thông thường
}
Toán tử eq so sánh hai xâu. Nếu chúng bằng nhau (từng kí tự một, và có
cùng chiều dài), thì kết quả là đúng. (Không có toán tử này trong C, và awk phải
dùng cùng toán tử cho xâu và số và tạo ra việc đoán có rèn luyện.)
Câu lệnh if chọn xem khối câu lệnh nào (giữa các dấu ngoặc nhọn sánh
đúng) là được thực hiện - nếu biểu thức là đúng, đó là khối thứ nhất, nếu không
thì đó là khối thứ hai.

Đoán từ bí mật
Nào, vì chúng ta đã có một tên nên ta hãy để cho một người chạy chương
trình đoán một từ bí mật. Với mọi người ngoại trừ Randal, chúng ta sẽ để cho
chương trình cứ hỏi lặp lại để đoán cho đến khi nào người này đoán được đúng.
Trước hết ta hãy xem chương trình này và rồi xem giải thích:
#! /usr/bin/perl
$secretword = “llama”; # từ bí mật
print “Tên bạn là gì?” ;
$name = <STDIN> ;
chop($name);
if ($name eq “Randal”) {
print “Xin chào, Randal! May quá anh ở đây!\n”;
} else {
print “Xin chào, $name!\n”; # chào thông thường
print “Từ bí mật là gì?” ;


$guess = <STDIN>;
chop($guess);
while ($guess ne $secrectword) {
print “Sai rồi, thử lại đi. Từ bí mật là gì?”;
$guess = <STDIN>;
chop($guess);
}
}
Trước hết, ta định nghĩa từ bí mật bằng việc đặt nó vào trong biến vô hướng
khác, $secretword. Sau khi đón chào, một người (không phải Randal) sẽ được
yêu cầu (với một câu lệnh print khác) đoán chữ. Lời đoán rồi được đem ra so
sánh với từ bí mật bằng việc dùng toán tử ne, mà sẽ cho lại đúng nếu các xâu
này không bằng nhau (đây là toán tử logic ngược với toán tử eq). Kết quả của

việc so sánh sẽ kiểm soát cho trình while, chu trình này thực hiện khối thân cho
tới khi việc so sánh vẫn còn đúng.
Tất nhiên, chương trình này không phải là an toàn lắm, vì bất kì ai mệt với
việc đoán cũng đều có thể đơn thuần ngắt chương trình và quay trở về lời nhắc,
hay thậm chí còn nhìn qua chương trình gốc để xác định ra từ. Nhưng, chúng ta
hiện tại chưa định viết một hệ thống an toàn, chỉ xem như một thí dụ cho trang
hiện tại của cuốn sách này.
Nhiều từ bí mật
Ta hãy xem cách thức mình có thể sửa đổi đoạn chương trình này để cho
phép có nhiều từ bí mật. Bằng việc dùng điều ta đã thấy, chúng ta có thể so sánh
lời đoán lặp đi lặp lại theo một chuỗi câu trả lời rõ được cất giữ trong các biến
vô hướng tách biệt. Tuy nhiên, một danh sách như vậy sẽ khó mà thay đổi hay
đọc vào từ một tệp hay máy tính trên cơ sở ngày làm việc thường lệ.
Một giải pháp tốt hơn là cất giữ tất cả các câu trả lời có thể vào trong một
cấu trúc dữ liệu gọi là danh sách hay mảng. Mỗi phần tử của mảng đều là một
biến vô hướng tách biệt mà có thể được đặt giá trị hay thâm nhập độc lập. Toàn
bộ mảng cũng có thể được trao cho một giá trị trong một cú đột nhập. Ta có thể
gán một giá trị cho toàn bộ mảng có tên @words sao cho nó chứa ba mật hiệu
tốt có thể có:
@words = (“camel”, “llama”, “oyster”);
Tên biến mảng bắt đầu với @, cho nên chúng là khác biệt với các tên biến
vô hướng.
Một khi mảng đã được gán thì ta có thể thâm nhập vào từng phần tử bằng
việc dùng một tham khảo chỉ số. Cho nên $words[0] là camel, $words[1] là
llama, $words[2] là oyster. Chỉ cố cũng có thể là một biểu thức, cho nên nếu ta
đặt $i là 2 thì $words[$i] là oyster. (Tham khảo chỉ số bắt đầu với $ thay vì @
vì chúng tham khảo tới một phần tử riêng của mảng thay vì toàn bộ mảng.)
Quay trở lại với thí dụ trước đây của ta:



#! /usr/bin/perl
$words = (“camel”, “llama”, “oyster”); # từ bí mật
print “Tên bạn là gì?” ;
$name = <STDIN> ;
chop($name);
if ($name eq “Randal”) {
print “Xin chào, Randal! May quá anh ở đây!\n”;
} else {
print “Xin chào, $name!\n”; # chào thông thường
print “Từ bí mật là gì?” ;
$guess = <STDIN>;
chop($guess);
$i = 0; # thử từ này trước hết
$correct = “ có thể”; # từ đoán có đúng hay không?
while ($correct eq $guess) { # cứ kiểm tra đến khi biết
if ($words[$i] eq $guess) { # đúng không
$correct = “có”; # có
} elsif ($i < 2) { # cần phải xét thêm từ nữa?
$i = $i + 1; # nhìn vào từ tiếp lần sau
} else # hết rồi, thế là hỏng
print “Sai rồi, thử lại đi. Từ bí mật là gì?”;
$guess = <STDIN>;
chop($guess);
$i = 0; # bắt đầu kiểm tra từ đầu lần nữa
}
} # kết thúc của while not correct

} # kết thúc của “not Randal”
Bạn sẽ để ý rằng chúng ta đang dùng biến vô hướng $correct để chỉ ra rằng
chúng ta vẫn đang tìm ra mật hiệu đúng, hay rằng chúng ta không tìm thấy.

Chương trình này cũng chỉ ra khối elsif của câu lệnh if-then-else. Không có
lệnh nào tương đương như thế trong C hay awk - đó là việc viết tắt của khối
else cùng với một điều kiện if mới, nhưng không lồng bên trong cặp dấu ngoặc
nhọn khác. Đây chính là cái rất giống Perl để so sánh một tập các điều kiện
trong một dây chuyền phân tầng if-elsif-elsif-elsif-else.
Cho mỗi người một từ bí mật khác nhau
Trong chương trình trước, bất kì người nào tới cũng đều có thể đoán bất kì
từ nào trong ba từ này và có thể thành công. Nếu ta muốn từ bí mật là khác nhau
cho mỗi người, thì ta cần một bảng sánh giữa người và từ:
Người

Từ bí mật

Fred

Camel


Barney

Llama

Betty

Oyster

Wilma

Oyster


Chú ý rằng cả Betty và Wilma đều có cùng từ bí mật. Điều này là được.
Cách dễ nhất để cất giữ một bảng như thế trong Perl là bằng một mảng kết
hợp. Mỗi phần tử của mảng này giữ một giá trị vô hướng tách biệt (hệt như kiểu
mảng khác), nhưng các mảng lại được tham khảo tới theo khoá, mà có thể là bất
kì giá trị vô hướng nào (bất kì xâu hay số, kể cả số không nguyên và giá trị âm).
Để tạo ra một mảng kết hợp được gọi là %words (chú ý % chứ không phải là
@) với khoá và giá trị được cho trong bảng trên, ta gán một giá trị cho %words
(như ta đã làm nhiều trước đây với mảng khác):
%words = (“fred”, “camel”, “barney”, “llama”,
“betty”, “oyster”, “wilma”, “oyster”) ;
Mỗi cặp giá trị trong danh sách đều biểu thị cho một khoá và giá trị tương
ứng của nó trong mảng kết hợp. Chú ý rằng ta đã bẻ phép gán này ra hai dòng
mà không có bất kì loại kí tự nối dòng nào, vì khoảng trắng nói chung là vô
nghĩa trong chương trình Perl.
Để tìm ra từ bí mật cho Betty, ta cần dùng Betty như khoá trong một tham
khảo vào mảng kết hợp %words, qua một biểu thức nào đó như
%words{“betty”}. Giá trị của tham khảo này là oyster, tương tự như điều ta đã
làm trước đây với mảng khác. Cũng như trước đây, khoá có thể là bất kì biểu
thức nào, cho nên đặt $person với betty và tính $words{$person} cũng cho
oyster.
Gắn tất cả những điều này lại ta được chương trình như thế này:
#! /usr/bin/perl
%words = (“fred”, “camel”, “barney”, “llama”,
“betty”, “oyster”, “wilma”, “oyster”) ;
print “Tên bạn là gì?” ;
$name = <STDIN> ;
chop($name);
if ($name eq “Randal”) {
print “Xin chào, Randal! May quá anh ở đây!\n”;
} else {

print “Xin chào, $name!\n”; # chào thông thường
$secretword = $words{$name}; # lấy từ bí mật
print “Từ bí mật là gì?” ;
$guess = <STDIN>;
chop($guess);
while ($correct ne $secretwords) {


print “Sai rồi, thử lại đi. Từ bí mật là gì?”;
$guess = <STDIN>;
chop($guess);
} # kết thúc của while
} # kết thúc của “not Randal”
Bạn hãy chú ý nhìn vào từ bí mật. Nếu không tìm thấy từ này thì giá trị của
$secretword sẽ là một xâu rỗng*, mà ta có thể kiểm tra liệu ta có muốn xác
định một từ bí mật mặc định cho ai đó khác không. Đây là cách xem nó:

[... phần còn lại của chương trình đã bị xoá...]
$secretword = $words{$name}; # lấy từ bí mật
if ($secretword eq “”) { # ấy, không thấy
$secretword = “đồ cáu kỉnh”; # chắc chắn, sao không là vịt?
}
print “Từ bí mật là gì?” ;

[... phần còn lại của chương trình đã bị xoá...]
Giải quyết định dạng cái vào thay đổi
Nếu tôi đưa vào Randal L. Schwartz hay randal thay vì Randal thì tôi sẽ
bị đóng cục lại với phần người dùng còn lại, vì việc so sánh eq thì lại so sánh
đúng sự bằng nhau. Ta hãy xem một cách giải quyết cho điều đó.
Giả sử tôi muốn tìm bất kì xâu nào bắt đầu với Randal, thay vì chỉ là một

xâu bằng Randal. Tôi có thể làm điều này trong sed hay awk hoặc grep với
một biểu thức chính qui: một tiêu bản sẽ xác định ra một tập hợp các xâu sánh
đúng. Giống như trong sed hay grep, biểu thức chính qui trong Perl để sánh bất
kì xâu nào bắt đầu với Randal là ^Randal. Để sánh xâu này với xâu trong
$name, chúng ta dùng toán tử sánh như sau:
if ($name =~ /^Randal/) {

## có, sánh đúng
} else {

## không, sánh sai
}
Chú ý rằng biểu thức chính qui được định biên bởi dấu sổ chéo. Bên trong
các dấu sổ chéo, dấu cách và khoảng trắng là có nghĩa, hệt như chúng ở bên
trong xâu.
Điều này gần như thế, nhưng nó lại không giải quyết việc lựa ra randal hay
loại bỏ Randall. Để chấp nhận randal, chúng ta thêm tuỳ chọn bỏ qua hoa
thường, một chữ i nhỏ được thêm vào sau dấu sổ chéo đóng. Để loại bỏ
Randall, ta thêm một đánh dấu đặc biệt định biên từ (tương tự với vi và một số
bản của grep) dưới dạng của \b trong biểu thức chính qui. Điều này đảm bảo
rằng kí tự đi sau l đầu tiên trong biểu thức chính qui không phải là một kí tự
*

Được, đấy chính là giá trị undef, nhưng nó trông như một xâu rỗng cho toán tử eq


khác. Điều này làm thay đổi biểu thức chính qui thành /^randal\b/i, mà có nghĩa
là “randal tại đầu xâu, không có kí tự hay chữ số nào theo sau, và chấp nhận cả
hai kiểu chữ hoa thường.”
Khi gắn tất cả lại với phần còn lại của chương trình thì nó sẽ giống như thế

này:
#! /usr/bin/perl
%words = (“fred”, “camel”, “barney”, “llama”,
“betty”, “oyster”, “wilma”, “oyster”) ;
print “Tên bạn là gì?” ;
$name = <STDIN> ;
chop($name);
if ($name =~ /^randal\b/i ) {
print “Xin chào, Randal! May quá anh ở đây!\n”;
} else {
print “Xin chào, $name!\n”; # chào thông thường
$secretword = $words{$name}; # lấy từ bí mật
if ($secretword eq “”) { # ấy, không thấy
$secretword = “đồ cáu kỉnh”; # chắc chắn, sao không là vịt?
}
print “Từ bí mật là gì?” ;
$guess = <STDIN>;
chop($guess);
while ($correct ne $secretwords) {
print “Sai rồi, thử lại đi. Từ bí mật là gì?”;
$guess = <STDIN>;
chop($guess);
} # kết thúc của while
} # kết thúc của “not Randal”
Như bạn có thể thấy, chương trình này khác xa với chương trình đơn giản
Xin chào, mọi người, nhưng nó vẫn còn rất nhỏ bé và làm việc được, và nó quả
làm được tí chút với cái ngắn xíu vậy. đây chính là cách thức của Perl.
Perl đưa ra tính năng về các biểu thức chính qui có trong mọi trình tiện ích
UNIX chuẩn (và thậm chí trong một số không chuẩn). Không chỉ có thế, nhưng
cách thức Perl giải quyết cho việc đối sánh xâu là cách nhanh nhất trên hành tin

này, cho nên bạn không bị mất hiệu năng. (Một chương trình giống như grep
được viết trong Perl thường đánh bại chương trình grep được các nhà cung cấp
viết trong C với hầu hết các cái vào. Điều này có nghĩa là thậm chí grep không
thực hiện một việc của nó thật tốt.)
Làm cho công bằng với mọi người
Vậy bây giờ tôi có thể đưa vào Randal hay randal hay Randal L.
Schwartz, nhưng với những người khác thì sao? Barney vẫn phải nói đúng


barney (thậm chí không được có barney với một dấu cách theo sau).
Để công bằng cho Barey, chúng ta cần nắm được từ đầu của bất kì cái gì
được đưa vào, và rồi chuyển nó thành chữ thường trước khi ta tra tên trong
bảng. Ta làm điều này bằng hai toán tử: toán tử substitute, tìm ra một biểu thức
chính qui và thay thế nó bằng một xâu, và toán tử translate, để đặt xâu này thành
chữ thường.
Trước hết, toán tử thay thế: chúng ta muốn lấy nội dung của $name, tìm kí tự
đầu tiên không là từ, và loại đi mọi thứ từ đây cho đến cuối xâu. /\W.*/ là một
biểu thức chính qui mà ta đang tìm kiếm - \W viết tắt cho kí tự không phải là từ
(một cái gì đó không phải là chữ, chữ số hay gạch thấp) và .* có nghĩa là bất kì
kí tự nào từ đấy tới cuối dòng. Bây giờ, để loại những kí tự này đi, ta cần lấy bất
kì bộ phận nào của xâu sánh đúng với biểu thức chính qui này và thay nó với cái
không có gì:
$name =~ s/\W.*//;
Chúng ta đang dùng cùng toán tử =~ mà ta đã dùng trước đó, nhưng bây giờ
bên vế phải ta có toán tử thay thế: chữ s được theo sau bởi một biểu thức chính
qui và xâu được định biên bởi dấu sổ chéo. (Xâu trong thí dụ này là xâu rỗng
giữa dấu sổ chéo thứ hai và thứ ba.) Toán tử này trông giống và hành động rất
giống như phép thay thế của các trình soạn thảo khác nhau.
Bây giờ để có được bất kì cái gì còn lại trở thành chữ thường thì ta phải dịch
xâu này dùng toán tử tr. Nó trông rất giống chỉ lệnh tr của UNIX, nhận một danh

sách các kí tự để tìm và một danh sách các kí tự để thay thế chúng. Với thí dụ
của ta, để đặt nội dung của $name thành chữ thường, ta dùng:
$name =~ tr/A-Z/a-z/;
Các dấu sổ chéo phân tách các danh sách kí tự cần tìm và cần thay thế. Dấu
gạch ngang giữa A và Z thay thế cho tất cả các kí tự nằm giữa, cho nên chúng ta
có hai danh sách, mỗi danh sách có 26 kí tự. Khi toán tử tr tìm thấy một kí tự từ
một xâu trong danh sách thứ nhất thì kí tự đó sẽ được thay thế bằng kí tự tương
ứng trong danh sách thứ hai. Cho nên tất cả các chữ hoa A trở thành chữ thường
a, và cứ như thế* .
Gắn tất cả lại với mọi thứ khác sẽ cho kết quả trong:
#! /usr/bin/perl
%words = (“fred”, “camel”, “barney”, “llama”,
“betty”, “oyster”, “wilma”, “oyster”) ;
print “Tên bạn là gì?” ;
$name = <STDIN> ;
chop($name);
$original_name = $name; # cất giữ để chào mừng
$name =~ s/\W.*//; # bỏ mọi thứ sau từ đầu
*

Các chuyên gia sẽ lưu ý rằng tôi cũng đã xây dựng một cái gì đó tựa như s/(\S*).*/\L$1/ để làm tất cả điều này
trong một cú đột kích, nhưng các chuyên gia có lẽ sẽ không đọc mục này.


$name =~ tr/A-Z/a-z/; # mọi thứ thành chữ thường
if ($name eq “randal” ) {
print “Xin chào, Randal! May quá anh ở đây!\n”;
} else {
print “Xin chào, $original_name!\n”; # chào thông thường
$secretword = $words{$name}; # lấy từ bí mật

if ($secretword eq “”) { # ấy, không thấy
$secretword = “đồ cáu kỉnh”; # chắc chắn, sao không là vịt?
}
print “Từ bí mật là gì?” ;
$guess = <STDIN>;
chop($guess);
while ($correct ne $secretwords) {
print “Sai rồi, thử lại đi. Từ bí mật là gì?”;
$guess = <STDIN>;
chop($guess);
} # kết thúc của while

} # kết thúc của “not Randal”
Bạn hãy để ý đến cách thức biểu thức chính qui sánh đúng với tên tôi
Randal đã lại trở thành việc so sánh đơn giản. Sau rốt, cả Randal L. Schwartz
và Randal đều trở thành randal sau khi việc thay thế và dịch. Và mọi người
khác cũng có được sự công bằng, vì Fred và Fred Flinstone cả hai đều trở
thành fred, Barney Rubble và Barney, the little guy sẽ trở thành barney, vân
vân.
Với chỉ vài câu lệnh, chúng ta đã tạo ra một chương trình thân thiện người
dùng hơn nhiều. Bạn sẽ thấy rằng việc diễn tả thao tác xâu phức tạp với vài nét
là một trong nhiều điểm mạnh của Perl.
Tuy nhiên, chém vào tên để cho ta có thể so sánh nó và tra cứu nó trong
bảng thì sẽ phá huỷ mất tên ta vừa đưa vào. Cho nên, trước khi chém vào tên,
cần phải cất giữ nó vào trong @original_name. (Giống như các kí hiệu C, biến
Perl bao gồm các chữ, chữ số và dấu gạch thấp và có thể có chiều dài gần như
không giới hạn.) Vậy ta có thể làm tham khảo tới $original_name về sau.
Perl có nhiều cách để giám sát và chặt cắt xâu. bạn sẽ thấy phần lớn chúng
trong Chương 7, Biểu thức chính qui và Chương 15, Việc chuyển đổi dữ liệu
khác.

Làm cho nó mô đun hơn một chút
Bởi vì chúng ta đã thêm quá nhiều mã nên ta phải duyệt qua nhiều dòng chi
tiết trước khi ta có thể thu được toàn bộ luồng chương trình. Điều ta cần là tách
bạch logic mức cao (hỏi tên, chu trình dựa trên từ bí mật đưa vào) với các chi
tiết (so sánh một từ bí mật với từ đã biết). Chúng ta có thể làm điều này cho rõ
ràng, hoặc có thể bởi vì một người đang viết phần cao cấp còn người khác thì


viết (hay đã viết) phần chi tiết.
Perl cung cấp các chương trình con có tham biến và giá trị cho lại. Một
chương trình con được xác định một khi nào đó trong chương trình, và rồi có thể
được dùng lặp đi lặp lại bằng việc gọi từ bên trong bất kì biểu thức nào.
Với chương trình nhỏ nhưng tăng trưởng nhanh của chúng ta, ta hãy tạo ra
một chương trình con tên là &good_word (tất cả các tên chương trình con đều
bắt đầu với một dấu và &) mà nhận một tên đã sạch và một từ đoán, rồi cho lại
true nếu từ đó là đúng, và cho lại false nếu không đúng. Việc xác định chương
trình con đó tựa như thế này:
sub good_word {
local($somename, $someguess) = @_; # tên tham biến
$somename =~ s/\W.*//; # bỏ mọi thứ sau từ đầu
$somename =~ tr/A-Z/a-z/; # mọi thứ thành chữ thường
if ($somename eq “randal”) { # không nên đoán
1; #giá trị cho lại là true
} elsif (($words{$somename} || “đồ cáu kỉnh”) eq $someguess) {
1; # giá trị cho lại là true
} else {
0; # cho lại giá trị false
}
}
Trước hết, việc định nghĩa ra một chương trình con bao gồm một từ dành

riêng sub đi theo sau là tên chương trình con (không có dấu và &) tiếp nữa là
một khối mã lệnh (được định biên bởi dấu ngoặc nhọn). Định nghĩa này có thể
để vào bất kì đâu trong tệp chương trình, nhưng phần lớn mọi người thích để
chúng vào cuối.
Dòng đầu tiên trong định nghĩa đặc biệt này là một phép gán làm việc sao
các giá trị của hai tham biến của chương trình này vào hai biến cục bộ có tên
$somename và $someguess. (local() xác định hai biến là cục bộ cho chương
trình con này, và các tham biến ban đầu trong một mảng cục bộ đặc biệt gọi là
@_.)
Hai dòng tiếp làm sạch tên, cũng giống như bản trước của chương trình.
Câu lệnh if-elsif-else quyết định xem từ được đoán ($someguess) là có
đúng cho tên ($somename) hay không. Randal không nên làm nó thành
chương trình con này, nhưng ngay cả nếu nó có, thì dù từ nào được đoán cũng
đều OK cả.
Biểu thức cuối cùng được tính trong chương trình con là cho lại giá trị.
Chúng ta sẽ thấy cách cho lại giá trị được dùng sau khi tôi kết thúc việc mô tả
định nghĩa về chương trình con.
Phép kiểm tra cho phần elsif trông có phức tạp hơn một chút - ta hãy chia nó
ra:


($words{$somename} || “đồ cáu kỉnh”) eq $someguess
Vật thứ nhất bên trong dấu ngoặc là mảng kết hợp quen thuộc của ta, cho
một giá trị nào đó từ %words dựa trên khoá $somename. Toán tử đứng giữa
giá trị đó và xâu đồ cáu kỉnh là toán tử || (phép hoặc logic) như được dùng
trong C và awk và các vỏ khác. Nếu việc tra cứu từ mảng kết hợp có một giá trị
(nghĩa là khoá $somename là trong mảng), thì giá trị của biểu thức là giá trị
đó. Nếu khoá không tìm được, thì xâu đồ cáu kỉnh sẽ được dùng thay. Đây
chính là một vật kiểu Perl thường làm - xác định một biểu thức nào đó, và rồi
đưa ra một giá trị mặc định bằng cách dùng || nếu biểu thức này có thể trở thành

sai.
Trong mọi trường hợp, dù đó là một giá trị từ mảng kết hợp, hay giá trị mặc
định đồ cáu kỉnh, chúng ta đều so sánh nó với bất kì cái gì được đoán. Nếu việc
so sánh là đúng thì ta cho lại 1, nếu không ta cho lại 0.
Bây giờ ta hãy tích hợp tất cả những điều này với phần còn lại của chương
trình:
#! /usr/bin/perl
%words = (“fred”, “camel”, “barney”, “llama”,
“betty”, “oyster”, “wilma”, “oyster”) ;
print “Tên bạn là gì?” ;
$name = <STDIN> ;
chop($name);
if ($name =~ /^randal\b/i ) { # trở lại cách khác
print “Xin chào, Randal! May quá anh ở đây!\n”;
} else {
print “Xin chào, $original_name!\n”; # chào thông thường
print “Từ bí mật là gì?” ;
$guess = <STDIN>;
chop($guess);
while ( ! &good_word($name, $guess)) {
print “Sai rồi, thử lại đi. Từ bí mật là gì?”;
$guess = <STDIN>;
chop($guess);
}
}
[ ... thêm vào định nghĩa của &good_word ở đây ...]
Chú ý rằng chúng ta đã quay trở lại với biểu thức chính qui để kiểm tra
Randal, vì bây giờ không cần kéo một phần tên thứ nhất và chuyển nó thành
chữ thường, chừng nào còn liên quan tới chương trình chính.
Sự khác biệt lớn là chu trình while có chứa &good_word. Tại đây, chúng ta

thấy một lời gọi tới chương trình con, truyền cho nó hai đối, $name và $guess.
Bên trong chương trình con này, giá trị của $somename được đặt từ tham biến
thứ nhất, trong trường hợp này là $name. Giống thế, $someguess được đặt từ


tham biến thứ hai, $guess.
Giá trị do chương trình con cho lại (hoặc 1 hoặc 0, nhớ lại định nghĩa đã nêu
trước đây) là âm với toán tử tiền tố ! (phép phủ định logic). Như trong C, toán tử
này cho lại đúng nếu biểu thức đi sau là sai, và ngược lại. Kết quả của phép phủ
định này sẽ kiểm soát chu trình while. Bạn có thể đọc điều này là “trong khi
không phải là từ đúng...”. Nhiều chương trình Perl viết tốt đọc rất giống tiếng
Anh, đưa lại cho bạn một chút tự do với Perl hay tiếng Anh. (Nhưng bạn chắc
chắn không đoạt giải Pulitzer theo cách đó.)
Chú ý rằng chương trình con này giả thiết rằng giá trị của mảng %words
được chương trình chính đặt. Điều này không đặc biệt là hay, nhưng chẳng có gì
so sánh được với static của C đối với chương trình con - nói chung, tất cả các
biến nếu không nói khác đi mà được tạo ra với toán tử local() thì đều là toàn cục
đối với toàn bộ chương trình. Trong thực tế, đây chỉ là vấn đề nhỏ, và người ta
thì có được tính sáng tạo về cách đặt tên biến dài hạn.
Chuyển danh sách từ bí mật vào tệp riêng biệt
Giả sử ta muốn dùng chung danh sách từ bí mật cho ba chương trình. Nếu ta
cất giữ danh sách từ như ta đã làm thì ta sẽ cần phải thay đổi tất cả ba chương
trình này khi Betty quyết định rằng từ bí mật của cô sẽ là swine thay vì oyster.
Điều này có thể thành phiền phức, đặc biệt khi xem xét tới việc Betty lại thường
xuyên thích thay đổi ý định.
Cho nên, ta hãy đặt danh sách từ vào một tệp, và rồi đọc tệp này để thu được
danh sách từ vào trong chương trình. Để làm điều này, ta cần tạo ra một kênh
vào/ra được gọi là tước hiệu tệp. Chương trình Perl của bạn sẽ tự động lấy ba
tước hiệu tệp gọi là STDIN, STDOUT và STDERR, tương ứng với ba kênh vào
ra chuẩn cho chương trình UNIX. Chúng ta cũng đã dùng tước hiệu STDIN để

đọc dữ liệu từ người chạy chương trình. Bây giờ, đấy chỉ là việc lấy một tước
hiệu khác để gắn với một tệp do ta tạo ra.
Sau đây là một đoạn mã nhỏ để làm điều đó:
sub init_words {
open (WORDSLIST, “wordslist”);
while ($name = <WORDSLIST>) {
chop ($name);
$word = <WORDSLIST>;
chop($word);
$words{$name} = $word;
}
close (WORDSLIST);
}
Tôi đặt nó vào một chương trình con để cho tôi có thể giữ phần chính của
chương trình không bị lộn xộn. Điều này cũng có nghĩa là vào thời điểm sau


(hướng dẫn: một vài lần ôn lại cuộc đi dạo này), tôi có thể thay đổi nơi cất giữ
danh sách từ, hay thậm chí định dạng của danh sách.
Định dạng được chọn bất kì cho danh sách từ là một khoản mục trên một
dòng, với tên và từ, luân phiên. Cho nên, với cơ sở dữ liệu hiện tại của chúng ta,
chúng ta có cái tựa như thế này:
fred
camel
barney
llama
betty
oyster
wilma
oyster

Toán tử open() tạo ra một tước hiệu tệp có tên WORDSLIST bằng cách liên
kết nó với một tệp mang tên wordslist trong danh mục hiện tại. Lưu ý rằng tước
hiệu tệp không có kí tự là lạ phía trước nó như ba kiểu biến vẫn có. Cũng vậy,
tước hiệu tệp nói chung là chữ hoa - mặc dầu chúng không nhất thiết phải là như
thế - bởi những lí do sẽ nêu chi tiết về sau.
Chu trình while đọc các dòng từ tệp wordslist (qua tước hiệu tệp
WORDSLIST) mỗi lần một dòng. Mỗi dòng đều được cất giữ trong biến
$name. Khi đạt đến cuối tệp thì giá trị cho lại bởi toán tử <WORDSLIST> là
xâu rỗng* , mà sẽ sai cho cho trình while, và kết thúc nó. Đó là cách chúng ta đi
ra ở cuối.
Mặt khác, trường hợp thông thường là ở chỗ chúng ta đã đọc một dòng (kể
cả dấu dòng mới) vào trong $name. Trước hết, ta bỏ dấu dòng mới bằng việc
dùng toán tử chop(). Rồi, ta phải đọc dòng tiếp để lấy từ bí mật, giữ nó trong
biến $word. Nó nữa cũng phải bỏ dấu dòng mới đi.
Dòng cuối cùng của chu trình while đặt $word vào trong %words với khoá
$name, cho nên phần còn lại của chương trình có thể thâm nhập vào nó về sau.
Một khi tệp đã được đọc xong thì có thể bỏ tước hiệu tệp bằng toán tử
close(). (Tước hiệu tệp dẫu sao cũng được tự động đóng lại khi chương trình
thoát ra, nhưng tôi đang định làm cho gọn.)
Định nghĩa chương trình con này có thể đi sau hay trước chương trình con
khác. Và chúng ta gọi tới chương trình con thay vì đặt %words vào chỗ bắt đầu
của chương trình, cho nên một cách để bao bọc tất cả những điều này có thể
giống thế này:
#! /usr/bin/perl
&init_words;
print “Tên bạn là gì?” ;
$name = <STDIN> ;
*

Về mặt kĩ thuật thì đấy lại là undef, nhưng cũng đủ gần cho thảo luận này



chop($name);
if ($name =~ /^randal\b/i ) { # trở lại cách khác
print “Xin chào, Randal! May quá anh ở đây!\n”;
} else {
print “Xin chào, $original_name!\n”; # chào thông thường
print “Từ bí mật là gì?” ;
$guess = <STDIN>;
chop($guess);
while ( ! &good_word($name, $guess)) {
print “Sai rồi, thử lại đi. Từ bí mật là gì?”;
$guess = <STDIN>;
chop($guess);
}
}
## chương trình con từ đây xuống
sub init_words {
open (WORDSLIST, “wordslist”);
while ($name = <WORDSLIST>) {
chop ($name);
$word = <WORDSLIST>;
chop($word);
$words{$name} = $word;
}
close (WORDSLIST);
}
sub good_word {
local($somename, $someguess) = @_; # tên tham biến
$somename =~ s/\W.*//; # bỏ mọi thứ sau từ đầu

$somename =~ tr/A-Z/a-z/; # mọi thứ thành chữ thường
if ($somename eq “randal”) { # không nên đoán
1; #giá trị cho lại là true
} elsif (($words{$somename} || “đồ cáu kỉnh”) eq $someguess) {
1; # giá trị cho lại là true
} else {
0; # cho lại giá trị false
}
}
Bây giờ nó bắt đầu trông giống một chương trình trưởng thành hoàn toàn.
Chú ý đến dòng thực hiện được đầu tiên là lời gọi tới &init_words. Không có
tham biến nào được truyền cả, cho nên chúng ta được tự do bỏ đi dấu ngoặc
tròn. Cũng vậy, giá trị cho lại không được dùng trong tính toán thêm, thì cũng là
tốt vì ta đã không cho lại điều gì đáng để ý. (Giá trị của close() thông thường là
đúng.)


Toán tử open() cũng được dùng để mở các tệp đưa ra, hay mở chương trình
như tệp (đã được biểu diễn ngắn gọn). Tuy thế, việc vét hết về open() sẽ được
nêu về sau trong cuốn sách này, trong Chương 10, Tước hiệu tệp và kiểm tra tệp.
Đảm bảo một lượng an toàn giản dị
“Danh sách các từ bí mật phải thay đổi ít nhất một lần mỗi tuần!” ông
Trưởng ban danh sách từ bí mật kêu lên. Thôi được, chúng ta không thể buộc
danh sách này khác đi, nhưng chúng ta có thể ít nhất cũng đưa ra một cảnh báo
nếu danh sách từ bí mật còn chưa được thay đổi trong hơn một tuần.
Cách tốt nhất để làm điều này là trong chương trình con &init_words chúng ta đã nhìn vào tệp ở đó. Toán tử Perl -M cho lại tuổi tính theo ngày từ một
tệp hay tước hiệu tệp đã được thay đổi từ lần trước, cho nên ta chỉ cần xem liệu
giá trị này có lớn hơn bẩy hay không đối với tước hiệu tệp WORDSLIST:
sub init_words {
open (WORDSLIST, “wordslist”);

if (-M WORDSLIST > 7) { # tuân thủ theo đường lối quan liêu
die “Rất tiếc, danh sách từ cũ hơn bẩy ngày rồi.”;
}
while ($name = <WORDSLIST>) {
chop ($name);
$word = <WORDSLIST>;
chop($word);
$words{$name} = $word;
}
close (WORDSLIST);
}
Giá trị của -M WORDSLIST được so sánh với bẩy, và nếu lớn hơn, thế thì ta
vi phạm vào đường lối rồi. Tại đây, ta thấy một toán tử mới, toán tử die, mà cho
in ra một thông báo trên thiết bị cuối* , và bỏ chương trình trong một cú bổ nhào
rơi xuống.
Phần còn lại của chương trình vẫn không đổi, cho nên trong mối quan tâm
tới việc tiết kiệm cây cối, tôi sẽ không lặp lại nó ở đây.
Bên cạnh việc lấy tuổi của tệp, ta cũng có thể tìm ra người chủ của nó, kích
cỡ, thời gian thâm nhập, và mọi thứ khác mà UNIX duy trì về tệp. Nhiều điều
hơn được trình bầy trong Chương 10.
Cảnh báo ai đó khi mọi việc đi sai
Ta hãy xem ta có thể làm cho hệ thống bị sa lầy thế nào khi ta gửi một mẩu
thư điện tử mỗi lần cho một ai đó đoán từ bí mật của họ không đúng. Ta cần sửa
đổi mỗi chương trình con &good_word (nhờ có tính mô đun) vì ta có tất cả
thông tin ngay đây.
*

Thực tại là STDERR, nhưng đấy thông thường là màn hình



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×