Diễn đàn tin học | Tutorial Room Mục lục »» Chương 2 Learning Perl - Chương 1: Giới thiệu qua về Perl 1. Lịch sử của Perl 2. Mục đích của Perl 3. Tính sẵn có 4. Hỗ trợ (nếu có khúc mắc) 5. Các khái niệm căn bản 6. Sơ qua về Perl 7. Bài tập 1.1 Lịch sử Perl PERL là cách viết tắt cho “Practical Extraction and Report Language”, mặc dù còn được gọi là “Pathologically Eclectic Rubbish Lister". Larry Wall đã 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 và đưa ra bản đầu tiên của Perl. Sau đó Larry đưa nó cho các độc giả Usenet, thường vẫn được gọi là “the Net”. Kết quả là Perl phát triển dần và cũng cùng tỉ lệ như kernel của UNIX. Nó đã phát triển các tính năng và tính khả chuyển. Larry sẽ đưa ra bản Perl mới nhất, bản 5.0, chắc chắn sẽ 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. Tuy nhiên, cuốn sách này đã được thử với Perl bản 4.0. 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 khác cần cho sự nâng cấp. 1.2 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 nhậy cảm với tính khả chuyển đối với trình shell, vì nó ngắn hơn và không phức tạp như các ngôn ngữ lập trinh C hay một ngôn ngữ công cụ UNIX nào khác. Khi đã quen thuộc với Perl, bạn sẽ mất ít thời gian để lấy được các trích dẫn dòng lệnh shell (hay khai báo C), và mất ít thời gian để lập trình vì Perl là một công cụ trợ giúp tuyệt vời. Các cấu trúc chặt chẽ của Perl cho phép tạo ra một số giải pháp với những công cụ mang tính tổng quát. Cũng vậy, bạn có thể lấy những công cụ này sang công việc tiếp, vì Perl có tính khả chuyển cao và lại có sẵn trên hầu hết các hệ thống. Giống như mọi ngôn ngữ khác, Perl có thể “chỉ viết” - tức là có thể viết ra chương trình cho máy hiểu mà con người không thể "đọc hiểu" được. Nhưng nếu chú ý cẩn thận, bạn có thể tránh được điều này. Thật vậy, đôi khi Perl trông như khó với những ai không quen, nhưng với người lập trình đã thạo Perl, nó cũng khá dễ (???). Nếu bạn làm theo những hướng dẫn trong cuốn sách này thì chương trình của bạn sẽ dễ đọc, dễ bảo trì và nâng cấp. 1.3 Tính sẵn có Nếu bạn nhận được một lỗi nhỏ: Perl: not found khi bạn thử gọi Perl từ dòng lệnh shell thì điều đó có nghĩa là Perl chưa được cài đặt trên hệ thống của bạn. Nhưng bạn hoàn toàn có thể lấy được Perl miễn phí vài cài vào hệ thốgn của mình. Perl được phân phối theo Giấy phép Công cộng GNU (GNU Public License), nghĩa là bạn có thể phân phát bản binary của Perl với điều kiện là phải kèm theo mã ngồn miễn phí; và nếu bạn sửa đổi Perl thep ý bạn bạn cũng phải phân phối mã nguồn của phần mà bạn sửa đổi. Bạn có thể lấy mã nguồn của Perl hoàn toàn miễn phí qua đường download mà chỉ tốn vài MB để lưu trữ và một khoảng thời gian để tải xuống. Trong thực tế, nó không chỉ là miễn phí mà 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. Đó là vì là Perl có 1 phần được gọi là "Cấu hình" sẽ có nhiệm vụ gọi 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, và cuối cùng nó sẽ 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, các "tín đồ" của Perl đã đem nó sang Amiga, Atari ST, họ Macintosh, VMS, OS/2, thậm chí MS/DOS, Windows - và có lẽ còn nhiều hơn nữa khi bạn đang đọc những dòng 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 phải hỏi trên nhóm tin Usenet chẳng hạn để có được thông tin mới nhất. Nếu bạn là một newbie, 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), hoặc bạn có thể tìm và download ở http://www.perl.org/. 1.4 Hỗ trợ (nếu như có khúc mắc) Bạn có thể liên hệ trực tiếp với Larry hoặc 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. hoặc gửi yêu cầu tới perl-users-request@virgina.edu. 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). 1.5 Các khái niệm cơ bản về Perl Một kịch bản shell là một dãy các lệnh shell đưa vào trong một tệp văn bản. Tệp này chạy bằng cách bật một bit thực hiện (qua chmod +x filename) rồi gõ tên của tệp đó tại dấu nhắc của shell. Chẳng hạn, một kịch bản shell để chạy lệnh date và sau đó là lệnh who sẽ được viết như sau: $ echo date > somecript $ echo who >> somecript $ cat somescript date who $ chmod +x somescript $ somescript [output of date followed by who] $ Tương tự, một chương trình Perl là một bó các câu lệnh và định nghĩa Perl được đưa 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 shell, nên chúng ta cần một bước phụ: đặt #!/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 được cài vào nơi không chuẩn (không phải là /usr/bin), hay hệ thống tựa UNIX của bạn không hiểu dòng #!, thì bạn hỏi người quản trị hệ thống của bạn để được giúp đỡ. Các thí dụ trong sách này giả sử rằng hệ thống của bạn đã được cài đặt Perl vào /usr/bin. Perl là một ngôn ngữ phi định dạng kiểu như C - khoảng trắng giữa các phần tử của chương trình là tuỳ chọn, trừ phi hai phần tử của chương trình dính liền với nhau có thể bị lầm lẫn thành một cá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, xuống dòng, về đầu dòng hay sang trang mới). 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 đừng lo, tài liệu sẽ chỉ rõ cho bạn biết là các khoảng trắng phải được đặt như thế nào với số lượng bao nhiêu. Có thể giả thiết rằng loại và số lượng khoảng trắng giữa các phần tử trong chương trình là tuỳ ý trong các trường hợp khác. Mặc dù 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, nhưng một chương trình Perl điển hình được viết xuống dòng và canh lề như chương trình C, với những phần câu lệnh lồng nhau được viết vào sâu hơn một chút chẳng hạn, sẽ làm cho chương trình của bạn dễ nhìn và dễ hiểu hơn. Cũng giống như một kịch bản shell, 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ề hàm chính main như trong C. Chú thích của Perl giống như chú thích của kịch bản shell: bất kì cái gì nằm giữa một dấu # tới cuối dòng đều là một chú thích. Perl không có chú thích trên nhiều dòng như C. Không giống hầu hết các shell (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 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 sẽ được lược bỏ 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 nhất đơn thuần dựa trên nền tảng là C được coi là trình biên dịch. Việc biên dịch này khá tiêu tốn thời gian. Và sẽ là phi hiệu quả nếu một chương trình Perl lớn lại chỉ thực hiện một nhiệm vụ nhỏ bé (trong số nhiều nhiệm vụ tiềm năng) và rồi thoát, vì thời gian chạy cho chương trình sẽ "nhỏ xíu" nếu so với thời gian biên 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 chiếm không gian đĩa. Hiểu theo một cách nào đó, nó là tốt nhất cho cả hai loại này. 1.6 Sơ lược về Perl Sau đâ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 ngắn gọn - mỗi vùng chủ đề đều được thảo luận chi tiết hơn nhiều về sau trong cuốn sách này. Nhưng việc "xem qua" này sẽ cho bạn kinh nghiệm nhanh chóng về ngôn ngữ. 1.6.1 Chương trình “Hello” Ta hãy nhìn vào một chương trình nhỏ sau: #!/usr/bin/perl print "Hello!\n"; Dòng đầu tiên báo đây là chương trình Perl. Nó cũng là lời chú thích cho Perl cho tới cuối dòng, giống như hầu hết các kịch bản shell 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. Hàm print bắt đầu chương trình, và nó có một đối số là 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. Giống như trong C, tất cả các câu lệnh đơn giản đều kết thúc bằng dấu chấm phảy (;). Khi bạn gọi chương trình này, phần kernel sẽ gọi bộ thông dịch Perl, phân tích toàn bộ chương trình (hai dòng, kể cả dòng chú thích đầu tiên) và thực hiện dạng đã dịch. Thao tác đầu tiên và duy nhất là thực hiện hàm print. Sau khi chương trình đã hoàn tất, thì tiến trình Perl sẽ trả về một mã cho shell để báo rằng chương trình đã kết thúc. 1.6.2 Hỏi câu hỏi và nhớ kết quả Ta hãy viết lại chương trình ở trên một chút, thay vì Hello trống không, chương trình sẽ chào tên người nhập vào từ bàn phím. Để làm việc này, ta cần một chỗ lưu giữ tên, một cách hỏi tên, và một cách nhận câu trả lời. Để đặt chỗ giữ giá trị (VD: tên) ta dùng 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 (xem chi tiết trong chương sau: Dữ liệu vô hướng, về những gì mà biến này có thể giữ, và những gì có thể làm với chúng). Bây giờ, giả sử rằng bạn có thể giữ một số hay một 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 vào. Chương trình trước đã chỉ ra cho ta cách nhắc - dùng hàm print. Và để nhận một dòng từ thiết bị cuối ta dùng toán tử <STDIN>. Ta dữ liệu nhập vào cho biến $name: print "Ten ban la gi? "; $name = <STDIN>; Giá trị của $name tại điểm này có một ký tự xuống dòng ở cuối (vì bạn phải nhấn Enter để kết thúc việc nhập tên). Để bỏ qua kí tự đó, chúng ta dùng hàm chop(). Hàm này lấy một biến vô hướng làm đối số duy nhất và bỏ đi ký tự cuối từ giá trị xâu của biến: chop($name); Sau đó in ra câu chào: print "Xin chao ban $name"; Và ta có chương trình hoàn chỉnh: #!/usr/bin/perl print "Ten ban la gi? "; $name = <STDIN>; chop($name); print "Xin chao ban $name!\n"; 1.6.3 Bổ sung chọn lựa Bây giờ ta muốn có một lời chào đặc biệt cho Jenny, 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 Jenny, và nếu hai xâu trùng khớp, thì làm điều gì đó đặc biệt. Ta hãy bổ sung thêm lệnh if-then-else và phép so sánh vào chương trình: #!/usr/bin/perl print "Ten ban la gi? "; $name = <STDIN>; chop($name); if ($name eq "Jenny") { #chao Jenny print "Chao Jenny! Lam bai tap di chu!\n"; } else { #chao binh thuong print "Xin chao ban $name!\n"; } Toán tử eq so sánh hai xâu. Nếu bằng nhau (từng kí tự một, và có cùng chiều dài) kết quả sẽ là True, ngược lại sẽ là False. Câu lệnh if chọn xem "khối" câu lệnh nào so sánh đúng được thực hiện - nếu biểu thức ($name eq "Jenny") là True, khối chưa câu lệnh print "Chao Jenny! Lam bai tap di chu!\n"; sẽ được thực hiện, còn trong trường hợp ngược lại sẽ là khối chưa câu lệnh print "Xin chao ban $name!\n";. 1.6.4 Đoán từ bí mật Để một người chạy chương trình đoán một từ bí mật. Với mọi người trừ Jenny, ta hãy để cho cho chương trình cứ hỏi lặp lại để đoán đến khi nào người này đoán được đúng. Hãy xem chương trình sau: #!/usr/bin/perl $secretword = "Jenny"; #tu bi mat print "Ten ban la gi? "; $name = <STDIN>; chop($name); if ($name eq "Jenny") { print "Chao Jenny! Lam bai tap di chu!\n"; } else { print "Xin chao ban $name!\n"; print "Ban thu doan xem toi ten la gi? "; $guess = <STDIN>; chop($guess); while ($guess ne $secretword) { print "Sai roi, doan lai di"; $guess = <STDIN>; chop($guess); } } Trước hết, ta định nghĩa tên cần đoán 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 Jenny) sẽ được yêu cầu (với một câu lệnh print khác) đoán chữ. Lời đoán được đem ra so sánh với từ cần đoán bằng việc dùng toán tử ne, mà sẽ cho True nếu các xâu này không bằng nhau (ne là toán tử ngược với toán tử eq). Kết quả của việc so sánh sẽ kiểm soát while, chu trình này thực hiện khi việc so sánh vẫn còn True. 1.6.5 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 thiết bị khác. 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 truy cập độc lập. Toàn bộ mảng cũng có thể được trao cho một giá trị để ta có thể "truy nhập" vào. 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: @words =("littlecat", "smalldog", "bigmouse"); 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ể truy cập đến từng phần tử bằng cách dùng một tham khảo chỉ số. Cho nên $words[0] là littlecat, $words[1] là smalldog, $words[2] là bigmouse. Chỉ số 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à bigmouse. (Tham khảo chỉ số bắt đầu với $ thay vì @ là do 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 #cac tu bi mat @words = ("littlecat", "smalldog", "bigmouse"); print "Ten ban la gi? "; $name = <STDIN>; chop($name); if ($name eq "Jenny") { print "Chao Jenny! Lam bai tap di chu!\n"; } else { print "Xin chao ban $name!\n"; print "Tu bi mat la gi"; $guess = <STDIN>; chop($guess); $i = 0; #thu truoc 1 tu $correct = "co the"; #tu nay co doan dung hay khong? while ($correct eq "co the") { #cu kiem tra den khi het if ($words[$i] eq $guess) { $correct = "co"; #dung roi } elsif ($i < 2) { $i = $i + 1; #xet tu tiep theo } else { print "Sai roi, thu lai di. Tu bi mat ma gi? "; $guess = <STDIN>; chop($guess); $i = 0; #kiem tra lai tu dau 1 lan nua } } #key thuc cua while ($correct eq "co the") } #Ket thuc cua "not Jenny" Chú ý 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, hoặc chúng ta không tìm thấy. Chương trình này cũng giới thiệu 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 else và theo sau là một if mới nhưng không lồng trong cặp dấu () 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 việc phân tầng if-elsif-elsif-elsif-else. 1.6.6 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 so sánh giữa người và từ: Người Từ bí mật Chip littlecat Dale smalldog Tom bigmouse Jerry bigmouse Chú ý rằng Jerry và Tom có chung từ bí mật là bigmouse; điều này hoàn toàn hợp lệ. Cách dễ nhất để cất giữ một bảng như thế trong Perl là bằng một "mảng băm" (hash array). Mỗi phần tử của mảng này giữ một giá trị vô hướng tách biệt (giống như kiểu mảng khác), nhưng các mảng lại được tham khảo theo khoá, 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 băm %words (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 = ( "Chip" , "littlecat", "Dale" , "smalldog", "Tom" , "bigmouse", "Jerry", "bigmouse" ); 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 băm. Chú ý rằng ta đã chia phép gán này ra 4 dòng mà không có bất kì loại kí tự nối dòng nào, vì khoảng trắng giữa các phần tử chương trình nói chung là vô nghĩa trong Perl. Để tìm ra từ bí mật cho Tom, ta cần dùng Tom 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{"Tom"}. Giá trị của tham khảo này là bigmouse, 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 gán $person bằng Tom và $words{$person} cũng sẽ trả về giá trị bigmouse. Chương trình hoàn chỉnh như sau: #!/usr/bin/perl %words = ( "Chip" , "littlecat", "Dale" , "smalldog", "Tom" , "bigmouse", "Jerry", "bigmouse" ); print "Ten ban la gi? "; $name = <STDIN>; chop($name); if ($name eq "Jenny") { print "Chao Jenny! Lam bai tap di chu!\n"; } else { print "Xin chao ban $name!\n"; #chao thuong thuong $secretword = $words{$name}; #lay tu bi mat print "Tu bi mat la gi? "; $guess = < STDIN > ; . 2 Learning Perl - Chương 1: Giới thiệu qua về Perl 1. Lịch sử của Perl 2. Mục đích của Perl 3. Tính sẵn có 4. Hỗ trợ (nếu có khúc mắc) 5. Các khái niệm căn bản 6. Sơ qua về Perl 7. Bài tập 1. 1. liên lạc thông qua nhóm tin Usenet comp.lang .perl. hoặc gửi yêu cầu tới perl- users-request@virgina.edu. 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á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 khác cần cho sự nâng cấp. 1. 2 Mục đích của Perl Perl được thiết kế