Giáo trình Thực hành ngôn ngữ lập trình

PHẦN I. PROLOG _______________________________________________________ 1

Chương I. Vị từ (predicate) - Tư duy lập trình và định nghĩa vấn đề trên Prolog ______ 2

Chương II. Các clause, cách giải thích các vấn đề trên Prolog _______________________ 5

Chương III. Môi trường lập trình B-Prolog _______________________________________ 7

III.1 Giới thiệu sơ nét về B-Prolog ____________________________________________________ 7

III.2 Cài đặt và làm việc với B-Prolog__________________________________________________ 7

III.3 Gỡ rối chương trình (debugging)__________________________________________________ 8

III.4 Các thuật ngữ cơ bản trong B-Prolog ______________________________________________ 9

III.5 Các kiểu dữ liệu và các vị từ xây dựng sẵn (built-in) cơ bản trong B-Prolog _______________ 10

Chương IV. Thực thi chương trình. - Đặt câu hỏi và nhận câu trả lời_________________ 12

Chương V. IV. Phép hợp nhất - Cơ chế tìm câu trả lời của Prolog. __________________ 15

V.1 Phép hợp nhất _______________________________________________________________ 15

V.2 Cơ chế tìm câu trả lời của Prolog ________________________________________________ 16

Chương VI. Sự quay lui - Khống chế số lượng lời giải -Vị từ nhát cắt và fail___________ 19

VI.1 Sự quay lui (back-tracing) trên Prolog_____________________________________________ 19

VI.2 Khống chế số lượng lời giải_____________________________________________________ 20

Chương VII. Lập trình đệ quy với Prolog ______________________________________ 22

Chương VIII. Danh sách trên Prolog ___________________________________________ 24

VIII.1 Cấu trúc của danh sách _____________________________________________________ 24

Chương IX. Lập trình đệ quy với danh sách trên Prolog ___________________________ 26

Chương X. Danh sách hai chiều _______________________________________________ 29

PHẦN II. LISP ________________________________________________________ 30

Chương I. Giới thiệu ________________________________________________________ 31

I.1 Lịch sử phát triển _______________________________________________________________ 31

I.2 Đặc điểm của gcLisp ____________________________________________________________ 31

1. Các đặc điểm của ngôn ngữ_____________________________________________________ 31

2. Kiểu dữ liệu _________________________________________________________________ 32

Chương II. Lập trình với gcLisp _______________________________________________ 33

II.1 Các khái niệm cơ bản _________________________________________________________ 33

1. Bắt đầu với LISP _____________________________________________________________ 33

2. Hàm và dữ liệu trong LISP _____________________________________________________ 34

3. Đánh giá____________________________________________________________________ 34

II.2 Các hàm xử lý trên danh sách ___________________________________________________ 34

1. FIRST và REST – CAR và CDR_________________________________________________ 34

2. CONS, APPEND, LIST________________________________________________________ 35ii

3. NTHCDR, BUTLAST và LAST _________________________________________________ 36

4. LENGTH và REVERSE _______________________________________________________ 37

II.3 Thao tác trên Integer, Ratio, Floating-Point Numbers, . ______________________________ 37

II.4 Lập trình hướng dữ liệu________________________________________________________ 38

1. ASSOC ____________________________________________________________________ 38

2. ACONS ____________________________________________________________________ 38

Chương III. Hàm và Biến cục bộ _______________________________________________ 40

III.1 Định nghĩa hàm – Chương trình đệ quy trong Lisp___________________________________ 40

III.2 Biến cục bộ _________________________________________________________________ 41

1. LET _______________________________________________________________________ 41

2. LET* ______________________________________________________________________ 42

Chương IV. Các vị từ và biểu thức điều kiện _____________________________________ 43

IV.1 Vị từ_______________________________________________________________________ 43

IV.2 Các phép so sánh: EQUAL, EQ, EQL và =_________________________________________ 43

IV.3 Vị từ MEMBER______________________________________________________________ 44

IV.4 Vị từ NULL và ENDP _________________________________________________________ 45

IV.5 Các vị từ xác định kiểu dữ liệu __________________________________________________ 45

IV.6 Các vị từ trên số______________________________________________________________ 47

IV.7 Các toán tử logic _____________________________________________________________ 48

1. AND ______________________________________________________________________ 48

2. OR ________________________________________________________________________ 49

3. NOT_______________________________________________________________________ 49

IV.8 Các dạng điều kiện ___________________________________________________________ 50

1. IF, WHEN và UNLESS________________________________________________________ 50

2. COND _____________________________________________________________________ 51

3. CASE______________________________________________________________________ 51

Chương V. Trừu tượng hóa dữ liệu ____________________________________________ 53

V.1 Các trường của một ký hiệu_____________________________________________________ 53

V.2 Doublets____________________________________________________________________ 53

1. Doublets____________________________________________________________________ 53

2. Pointed pair _________________________________________________________________ 54

3. Ký hiệu pointed pair __________________________________________________________ 54

4. Doublets trong LISP __________________________________________________________ 54

V.3 Lời gọi hàm tính toán _________________________________________________________ 55

1. Apply______________________________________________________________________ 55

2. Funcall _____________________________________________________________________ 55

V.4 Hàm vô danh ________________________________________________________________ 56

1. Lambda expression ___________________________________________________________ 56

2. Hàm vô danh và biến cục bộ ____________________________________________________ 56

Chương VI. Lặp trên số và trên danh sách _______________________________________ 57

VI.1 Các cấu trúc lặp ______________________________________________________________ 57

1. DOTIMES __________________________________________________________________ 57iii

2. DOLIST Hỗ trợ lặp trên danh sách _______________________________________________ 57

3. DO tổng quát hơn DOLIST và DOTIMES _________________________________________ 59

VI.2 Các dạng đặc biệt_____________________________________________________________ 59

1. progn ______________________________________________________________________ 59

2. prog1 ______________________________________________________________________ 59

Chương VII. Các thao tác với tập tin __________________________________________ 61

VII.1 Lưu lại tập tin chương trình và dữ liệu ____________________________________________ 61

VII.2 Biên dịch tập tin______________________________________________________________ 61

VII.3 Debugging __________________________________________________________________ 61

Chương VIII. Cài đặt và sử dụng gcLisp ________________________________________ 63

VIII.1 Cài đặt___________________________________________________________________ 63

VIII.2 Startup Gclisp _____________________________________________________________ 63

VIII.3 Phím nóng________________________________________________________________ 63

VIII.4 Dòng lệnh ________________________________________________________________ 64

VIII.5 Lệnh tiền tố (Prefix command) ________________________________________________ 64

VIII.6 Cửa sổ soạn thảo GMAC ____________________________________________________ 64

VIII.7 Load file vào gclisp_________________________________________________________ 64

PHẦN III. SMALLTALK_________________________________________________ 66

Chương I. LÝ THUYẾT VỀ OOP VÀ NGÔN NGỮ SMALLTALK ________________ 67

I.1 Lập trình hướng đối tượng (Object Oriented Programming) với Smalltalk ___________________ 67

1. Đối tượng (Object) - Các thành phần (member) của đối tượng: Các thuộc tính (properties) và các

phương thức (methods) - Sự bao đóng (encapsulation). ____________________________________ 67

2. Khái niệm class - Mối quan hệ giữa object và class - Khái niệm instance. _________________ 67

3. Phương thức - Thông điệp (message) - Đối tượng nhận thông điệp (receiver). Đối số của thông

điệp (argument) ___________________________________________________________________ 68

4. Các loại thông điệp: unary, binary và keyword. Độ ưu tiên giữa các thông điệp. ____________ 69

5. Câu lệnh (statement) - kịch bản (script)____________________________________________ 70

6. Che giấu thông tin (hiding information) ___________________________________________ 71

7. Sự thừa kế (inheritance) - Che phủ (override) - Sự dẩn xuất (derivation) - Mối quan hệ giữa các

đối tượng: cây các lớp. _____________________________________________________________ 71

8. Tính đa hình (polymorphism) - Sự ràng buộc muộn (late - binding)______________________ 72

I.2 Ngôn ngữ Smalltalk _____________________________________________________________ 72

1. Object trên Smalltalk. Thuộc tính thường và thuộc tính indexed. Các thành phần cho đối tượng và

thành phần cho lớp ________________________________________________________________ 72

2. Các literal - object: Integer, Float, Character, Boolean, Array, String, Context _____________ 73

3. Khai báo biến - Ràng buộc về kiểu trên ngôn ngữ Smalltalk - Phát biểu gán - Phát biểu trả về _ 74

4. Định nghĩa một object mới. Các phương thức new và new: ____________________________ 75

5. Định nghĩa một class mới và phương thức mới - Sự biên dịch offline và online một phương thức

76

6. Bên trong phương thức - Các từ khóa self và super___________________________________ 76

7. Các phương thức primitive _____________________________________________________ 78

8. Khái niệm về MetaClass - Sử dụng MetaClass - Lập trình OOP động (dynamic) với Smalltalk 78iv

9. Các lớp đặc biệt: Compiler, Window, ViewManager, Prompter. _______________________ 79

I.3 Một số kỹ thuật lập trình căn bản trên Smalltalk _______________________________________ 79

1. Sự mô phỏng các cấu trúc điều khiển. _____________________________________________ 79

2. Thao tác trên tập hợp (collection). Một số kỹ thuật xử lý trên tập hợp.____________________ 80

Chương II. HƯỚNG DẪN SỬ DỤNG VWIN VERSION 2.0 _______________________ 83

II.1 Hướng dẫn sử dụng chương trình VWIN: __________________________________________ 83

1. Thao tác trên hệ thống lớp ______________________________________________________ 83

2. Lập trình ___________________________________________________________________ 85

3. Load và Save file. ____________________________________________________________ 88

4. Gỡ rối______________________________________________________________________ 88

II.2 Giới thiệu về một số lớp có sẳn của VWIN_________________________________________ 90

1. Lớp Object__________________________________________________________________ 90

2. Lớp Magnitude ______________________________________________________________ 91

3. Lớp Number, Integer, Float, Character ____________________________________________ 91

4. Lớp IndexedCollection: ________________________________________________________ 91

5. Lớp Context: ________________________________________________________________ 94

Chương III. MỘT SỐ KỸ THUẬT LẬP TRÌNH CĂN BẢN VỚI LỚP COLLECTION

TRÊN SMALLTALK - VÍ DỤ VÀ BÀI TẬP _______________________________________ 96

III.1 Sử dụng phương thức do: ______________________________________________________ 96

1. Ví dụ:______________________________________________________________________ 96

2. Bài tập đề nghị: ______________________________________________________________ 96

III.2 Sử dụng phương thức select: hoặc reject: __________________________________________ 97

1. Ví dụ:______________________________________________________________________ 97

2. Bài tập đề nghị_______________________________________________________________ 97

III.3 Sử dụng phương thức collect: ___________________________________________________ 97

1. Ví dụ:______________________________________________________________________ 97

2. Bài tập đề nghị: ______________________________________________________________ 98

III.4 Bài tập tổng hợp: _____________________________________________________________ 98

1. Ví dụ:______________________________________________________________________ 98

2. Bài tập đề nghị_______________________________________________________________ 98

pdf103 trang | Chia sẻ: trungkhoi17 | Lượt xem: 411 | Lượt tải: 2download
Bạn đang xem trước 20 trang tài liệu Giáo trình Thực hành ngôn ngữ lập trình, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
rác (garbage collection), gọi hàm đệ quy, theo vết và dò lỗi (tracing and debugging) và trình soạn thảo theo cú pháp. I.2 Đặc điểm của gcLisp 1. Các đặc điểm của ngôn ngữ Từ khi được John McCarthy (MIT) nghĩ ra năm 1958, LISP được tinh chế dần đến version 1.5 và được sử sụng lâu dài về sau Lisp là ngôn ngữ hướng chức năng (functional language hay applicative), dùng lối ký hiệu tiền tố (prefix) và dấu ngoặc đơn: f(x,y, z) được ký hiệu là (f x y z) Tương tự x+y ký hiệu là (+ x y) Bt. ⎟⎠ ⎞⎜⎝ ⎛ + 2 3sin πx viết trong Lisp như thế nào ? ( )( )( )( )2 / 3 * sin pix+ Lisp là ngôn ngữ thông dịch (interpreted language) (xem Error! Reference source not found.) Ví dụ: Ngôn ngữ biên dịch câu lệnh (instructions) biên dịch chương trình thực thi thực thi kết quả Ngôn ngữ thông dịch Biểu thức đánh giá trả lời Kết quả vòng lặp top-level Hướng dẫn sử dụng Lisp 32 * (+ 3 4) 7 * (+ (* 3 4)(- 5 2)) 15 *4 4 2. Kiểu dữ liệu Lisp thao tác trên các loại dữ liệu: ¾ Biểu thức expression::= atom | list ¾ Danh sách list::= (expression1...expressionn) Danh sách là một chuỗi các biểu thức ngăn cách nhau bởi khoảng trắng, tất cả đặt trong dấu ngoặc đơn ¾ Atoms atom::= số | chuỗi ký tự | ký hiệu ¾ Ký hiệu (~ identifier): từ tạo bởi các ký tự bất kỳ, ngoại trừ ( ) ‘ ` “ ; và khoảng trắng ¾ Boolean: Lisp không có kiểu boolean. Trong Lisp, nil mang giá trị logic sai và tất cả các biểu thức khác có giá trị đúng. Ký hiệu t dùng để chỉ trị logic đúng theo mặc định. Các kiểu dữ liệu được xếp theo cấp bậc như sau: Biến trong Lisp không có kiểu dữ liệu , cùng một biến có thể có nhiều kiểu dữ liệu khác nhau. Ví dụ: * (setq a ‘(1 2 3)) (1 2 3) * a (1 2 3) * (setq a 2) 2 * a 2 expression list atom symbol real nillist ... number ... ... interger Hướng dẫn sử dụng Lisp 33 Chương II. Lập trình với gcLisp Bây giờ chúng ta sẽ cùng bắt đầu với LISP. Chương này sẽ giới thiệu một số hàm cơ bản nhằm thao tác trên ký hiệu trong LISP. Đầu tiên là các hàm về số, sau đó là các hàm trên danh sách. Phần này cũng sẽ giới thiệu một số thuật ngữ khi làm việc với LISP, chẳng hạn như dấu nhắc, chú thích, hàm, đối số, chương trình, giải thuật, atom, số, ký hiệu, danh sách, các phần tử trong danh sách, biểu thức, kiểu dữ liệu, dạng, đánh giá. II.1 Các khái niệm cơ bản 1. Bắt đầu với LISP Đối với phần lớn ngôn ngữ lập trình, cách tốt nhất để học là bắt đầu với một chương trình đơn giản. Trong phần này, chúng ta sẽ làm quen với một số khái niệm cơ bản trong LISP. Để bắt đầu, chúng ta tưởng tượng ta đang ngồi trước màn hình vi tính. Khi LISP không thực hiện một thao tác gì, nó ở trạng thái tĩnh. Khi đó, LISP hiển thị dấu nhắc cho biết nó đang đợi chúng ta gõ lệnh vào. Trong phần lớn các hệ COMMON LISP hiện nay, dấu nhắc chương trình là dấu sao. * Những từ đi sau dấu chấm phẩy là ghi chú chèn vào chương trình. Tất cả những từ còn lại sau dấu ; trong cùng dòng sẽ không được LISP xử lý. Chúng ta khởi đầu với một ví dụ đơn giản minh họa khả năng của LISP về xử lý số học * (+ 3.14 2.71) 5.85 Giả sử chúng ta muốn lưu trữ một số thông tin cần thiết của một người. Ví dụ, chúng ta muốn ghi nhớ danh sách bạn bè, kẻ thù, của một người nào đó. Chúng ta dùng SETF (viết tắt của set field). * (setf friends ‘(dick jane sally)) (DICK JANE SALLY) * friends (DICK JANE SALLY) * (setf enemies ‘(troll grinch ghost)) (TROLL GRINCH GHOST) * enemies (TROLL GRINCH GHOST) Hai danh sách trên là những thông tin động và có thể thay đổi. Ví dụ như ghost không còn là kẻ thù mà trở thành bạn bè. Khi đó cần cập nhật lại hai danh sách. * (setf enemies (remove ‘ghost enemies)) (TROLL GRINCH) * (setf friends (cons ‘ghost friends)) (GHOST DICK JANE SALLY) * enemies Hướng dẫn sử dụng Lisp 34 (TROLL GRINCH) * friends (GHOST DICK JANE SALLY) Bây giờ chúng ta sẽ xem làm thế nào định nghĩa một hàm làm công việc tương tự. Ta định nghĩa hàm tên NEWFRIEND thực hiện công việc đổi một người từ kẻ thù thành bạn bè. (defun newfriend (name) (setf enemies (remove name enemies)) (setf friends (cons name friends)) ) Với hàm NEWFRIEND, việc đổi tình trạng của GHOST từ kẻ thù thành bạn bè có thể thực hiện bằng một dòng lệnh đơn giản. * (newfriend ‘ghost) 2. Hàm và dữ liệu trong LISP Lisp là ngôn ngữ đặc trưng cho việc xử lý danh sách Chương trình được biểu diễn bằng các danh sách và có thể thao tác trên đó như dữ liệu (+ (* 3 4) (- 5 2)) chương trình: hàm + áp dụng vào hai đối số dữ liệu: danh sách gồm ba thành phần Ở top-level, khi đóng vai trò là đối số của một hàm, một danh sách luôn được xem như là sự áp dụng một hàm. 3. Đánh giá ‘Exp là cách viết tắt của (quote Exp) *‘a A *‘‘a (QUOTE A) QUOTE không đánh giá đối số Ngược lại với quote là hàm eval đánh giá giá trị của đối số * (setq l ‘(a b c)) (A B C) * (eval (list ‘car ‘l)) A * (eval (list ‘* (1+ 3) 2)) 6 Giá trị của (eval ‘Exp) là Exp II.2 Các hàm xử lý trên danh sách 1. FIRST và REST – CAR và CDR ¾ FIRST trả về phần tử đầu tiên của danh sách Hướng dẫn sử dụng Lisp 35 ¾ REST trả về danh sách theo sau phần tử đầu tiên Cho đến gần đây, phần lớn lập trình viên LISP vẫn dùng CAR và CDR thay cho FIRST và REST. Ngoài chức năng tương tự, CAR và CDR có thể kết hợp với nhau.thành dạng phức hợp CxxR, CxxxR hay CxxxxR. Mỗi x tượng trưng cho A – CAR hay D – CDR. Quy ước: * (car nil) NIL * (cdr nil) NIL Bài tập: 1. Lấy phần tử thứ ba của danh sách (car (cdr (cdr l))) có thể viết: (caddr l) CAR và CDR có thể kết hợp với nhau đến mức độ 4 Ví dụ: (caadr l) = (car (car (cdr l))) (cadar l) = (car (cdr (car l))) 2. Làm thế nào trích ra chuỗi example trong danh sách: L=((this) is (an (example)) more complex) L=((this) is (an (example)) more complex) (cdr l) = (is (an (example)) more complex) (cdr (cdr l)) = ((an (example)) more complex) (car (cdr (cdr l))) = (an (example)) (cdr (car (cdr (cdr l)))) = ((example)) (car (cdr (car (cdr (cdr l))))) = (example) (car (car (cdr (car (cdr (cdr l)))))) = example 2. CONS, APPEND, LIST ¾ LIST trả về danh sách các đối số * (list ‘a (+ 3 1) ‘c) (a 4 c) * (list ‘(a b) ‘(c d)) ((a b) (c d)) * (list ‘a nil) (a nil) ¾ CONS thêm một phần tử vào đầu danh sách (cons ‘a ‘(2 3)) (a 2 3) (cons `(a b) ‘(c d)) ((ab) c d) (list `a nil) Hướng dẫn sử dụng Lisp 36 (a) (CAR (CONS a l)) = a (CDR (CONS a l)) = l Bt. Cho biết giá trị của các biểu thức sau: 1. (cons ‘a (cons ‘b (cons ‘c nil))) (cons ‘a (cons ‘b (cons ‘c nil))) = (cons ‘a (cons ‘b ‘(c))) = (cons ‘a ‘(b c)) = (a b c) 2. (list (car ‘(car ((1) (2)))) (cdr (cdr ‘((1) (2))))) (list (car ‘(car ((1) (2)))) (cdr (cdr ‘((1) (2))))) = (list ‘car (cdr (cdr ‘((1) (2))))) = (list ‘car (cdr ((2)))) = (list ‘car nil) = (list ‘car nil) = (car nil) ¾ APPEND kết hợp các phần tử của mọi danh sách đã cho (setq l1 ‘(a b) l2 ‘(x y)) = (x y) (append l1 l2) = (a b x y) (append l1 ‘() l2 ‘()) = (a b x y) 3. NTHCDR, BUTLAST và LAST ¾ NTHCDR cắt n phần tử đầu danh sách, với thông số đầu chỉ số phần tử cần cắt * (setq l ‘(a b c d e)) (a b c d e) * (nthcdr 2 l) (c d e) ¾ BUTLAST cắt n phần tử cuối danh sách, với thông số đầu là danh sách, thông số thứ hai chỉ số phần tử cần cắt * (setq l ‘(a b c d e)) (a b c d e) * (butlast l 2) (a b c) * (butlast l 10) NIL ¾ LAST trả về danh sách tất cả phần tử trừ phần tử cuối cùng đã bị loại ra * (setq l ‘(a b c d e) l1 ‘((a b) (c d))) Hướng dẫn sử dụng Lisp 37 ((a b) (c d)) * (last l) (e) * (last l1) ((c d)) 4. LENGTH và REVERSE ¾ LENGTH trả về chiều dài của chuỗi * (setq l ‘(a b c d e)) (a b c d e) * (length l) 5 ¾ REVERSE trả về chuỗi nghịch đảo * (setq l ‘(a b c d e)) (a b c d e) * (reverse l) (e d c b a) II.3 Thao tác trên Integer, Ratio, Floating-Point Numbers, ... * (/ 1.234321 1.111) 1.111 * (/ 27 9) 3 * (\\ 10 4) 2 Tuy nhiên với trường hợp chia không chẵn, kết quả là một phân số: * (/ 22 7) 22/7 Dùng FLOAT nếu muốn kết quả trả về là số thực có dấu phẩy động: (float (/ 22 7)) 3.14286 Dùng ROUND để làm tròn kết quả: * (round (/ 22 7)) 3 ;Thương – số nguyên gần nhất 1/7 ;Phần dư * (+ round (/ 22 7)) (round (7/3))) 5 * (round (/ 5 2)) 2 Một số hàm tính toán học: * (MAX 2 4 3) 4 * (MIN 2 4 3) 2 Hướng dẫn sử dụng Lisp 38 * (expt 2 3) 8 * (expt 3 2) 9 * (expt 3.3 2.2) 13.827085 * (sqrt 9) 3 * (abs -5) 5 II.4 Lập trình hướng dữ liệu 1. ASSOC ¾ ASSOC gắn với một danh sách – association list hay còn gọi a-list (setf sarah ‘((height .54) (weight 4.4))) height và weight là khóa trong danh sách được gán cho SARAH; .54 và 4.4 là các giá trị biểu thị bằng met và kilograms. ¾ Có thể lấy các thành phần từ một danh sách dùng ASSOC với các đối số là một khóa và danh sách liên kết: Ví dụ: * (setf Andrew ‘((height .74) (weight 6.4))) ((HEIGHT 0.74) (WEIGHT 6.4)) * (assoc ‘height Andrew) (HEIGHT 0.74) Lưu ý ASSOC luôn trả về toàn bộ danh sách con tương ứng với khóa. Trong trường hợp có nhiều danh sách con cùng khóa, danh sách đầu tiên sẽ được trả về. 2. ACONS ¾ ACONS để thêm một thành phần mới vào danh sách Ví dụ: * (acons ‘nick ‘Bobby Andrew) ((NICK . BOBBY) (HEIGHT 0.74) (WEIGHT 6.4)) Key Key Value Value (ASSOC ) (ACONS ) Hướng dẫn sử dụng Lisp 39 ¾ Ví dụ: Chúng ta muốn dùng cùng một hàm thực hiện việc cộng hai số và nối hai chuỗi ¾ Giải pháp 1 (defun add(x y) (cond ((numberp x) (+ x y)) ((listp x) (append x y) ) ) ) ¾ Giải pháp 2 * (setf add ‘((FIXNUM +) (CONS append))) ((FIXNUM +) (CONS APPEND)) * (defun add(x y) (funcall (cadr (assoc (type-of x) add)) x y) ) funcall cho phép gọi các hàm tính toán Hướng dẫn sử dụng Lisp 40 Chương III. Hàm và Biến cục bộ Trong chương trước, chúng ta đã tìm hiểu về các hàm cơ bản cũng như một số thuật ngữ trên LISP. Chương này sẽ giúp chúng ta trong việc viết các chương trình riêng cho mình và định nghĩa một hàm mới dựa trên các hàm cơ bản. Các thuật ngữ mới cũng được thêm vào như biến, tham số, thân chương trình. III.1 Định nghĩa hàm – Chương trình đệ quy trong Lisp Trong Lisp, chúng ta định nghĩa hàm bằng từ khóa defun: (defun ) ký hiệu đại diện cho các biến biểu thức Ví dụ: * (defun square (x) (* x x)) SQUARE * (square 3) 9 * (defun abs(x) (if (>= x 0) x (* -1 x) ) ) ABS Vòng lặp trong Lisp được thực hiện chủ yếu nhờ vào đệ quy Ví dụ: Tính giai thừa Trong Pascal, hàm n! được viết bằng vòng lặp: function fac(integer:n):integer; var i:integer begin fac:=1; for i:=1 to n do fac:=fac*i; end Định nghĩa đệ quy của giai thừa: n!=1*2*...*n 0!=1 n! = *n 1*2*...*n-1 (n-1)! n!=n* (n-1) nếu n≥2 0!=1 Hướng dẫn sử dụng Lisp 41 Trong Lisp: (defun fac(n) (if (= n 0) 1 (* n fac (1- n)) ) ) Bài tập: 3. Viết hàm in ra phần tử thứ n trong danh sách (defun nth (n l) (if (= n 1) (car l) (nth (1- n) (cdr l)) ) ) 4. Ví dụ đệ quy chéo hay lời gọi đệ quy: (defun pair (n) (or (= n 0) (impair (1- n)) ) ) (defun impair (n) (and ( n 0) (pair (1- n)) ) ) III.2 Biến cục bộ 1. LET (let ((var1 exp1) (varm expm)) expm+1 expn) Chúng ta gán cho mỗi biến giá trị của biểu thức tương ứng, sau đó ta đánh giá (progn expm+1 expn) Ví dụ: * (let ((x (fac 4))) (* x x)) = 576 ¾ Các biến cục bộ che phủ các biến toàn cục * (setq x 5) 5 * (let ((x 1)) x) = 1 * x 5 * (let ((x 1)) (setq x 2) x) 2 *x 5 ¾ Các biến cục bộ che phủ các đối số của một hàm * (defun foo(x) (let ((x 1)) x ) ) FOO * (foo 4) Hướng dẫn sử dụng Lisp 42 1 ¾ Các liên kết được thực hiện song song * (defun bar(x) (let ((x 1) (y (1+ x))) y) ) BAR * (bar 4) 5 2. LET* ¾ Dạng let* thực hiện một liên kết tuần tự các đối số * (defun bar(x) (let ((x 1) (y (1- x))) y) ) BAR * (defun bar1(x) (let* ((x 1) (y (1- x))) y) ) BAR * (bar 3) 2 * (bar1 3) 0 ¾ let* tương ứng với let lồng nhau * (defun bar(x) (let ((x 1)) (let ((y (1+ x))) y) ) ) BAR Hướng dẫn sử dụng Lisp 43 Chương IV. Các vị từ và biểu thức điều kiện Mục đích chính của chương này là giải thích một số từ mà chúng ta gọi là vị từ. Những từ này, khi kết hợp với các biểu thức điều kiện sẽ cho phép chúng ta biến đổi những gì xảy ra trong một số tình huống, và nhờ vào đó, chúng ta có thể định ra các hàm phức tạp hơn. Một số vị từ còn cho phép thay đổi cách xử lý của các đối số. IV.1 Vị từ Một vị từ là một hàm trả về giá trị true hay false. FALSE trong LISP luôn được biểu diễn bằng NIL. True được biểu diễn bằng ký hiệu T, bất kỳ cái gì khác NIL đều được xem là có giá trị true. Lưu ý là ở đây, T và NIL là các ký hiệu đặc biệt trong đó giá trị của chúng đã được thiết lập sẵn cho T và NIL. Điều đó có nghĩa là giá trị của T là T và giá trị của NIL là NIL. Ví dụ: * t T ; Giá trị của T là T * nil NIL ; Giá trị của NIL là NIL IV.2 Các phép so sánh: EQUAL, EQ, EQL và = EQUAL kiểm tra xem hai đối số và xem giá trị của chúng có cùng một biểu thức biểu diễn hay không. EQUAL dùng cho cả atom và danh sách. * (equal (+ 2 2) 4) T * (equal (+ 2 3) 3) NIL * (equal ‘(this is a list) (setf l ‘(this is a list))) T * (equal ‘(this is a list) l) T * (equal ‘(this is a list) (setf reverse-of-l ‘(list a is this))) NIL * (equal l (reverse reverse-of-l)) T Các vị từ EQUAL, EQ, EQL và = đều có hai đối số và đều dùng để so sánh hai đối số. Nếu hai đối số là bằng nhau, vị từ sẽ trả về giá trị T; ngược lại vị từ trả về giá trị NIL. Tuy nhiên chúng có một số khác biệt trong cách thức so sánh. Nếu như EQUAL được xem là vị từ chung dùng để so sánh, thì EQ và = được sử dụng với những mục đích khác hơn: Vị từ Mục đích equal Giá trị của hai đối số có cùng là một biểu thức hay không ? Hướng dẫn sử dụng Lisp 44 eql eq = Giá trị của hai đối số có cùng là một ký hiệu hay một số hay không ? Giá trị của hai đối số có cùng là một ký hiệu hay không ? Giá trị của hai đối số có cùng là một số hay không ? Bây giờ, chúng ta xem xét một cách chi tiết hơn về liên hệ giữa các vị từ: ¾ EQUAL đầu tiên kiểm tra xem hai đối số có thỏa mãn EQL. Nếu không thỏa, nó sẽ xem chúng là các danh sách và kiểm tra từng phần tử trong nó có thỏa EQUAL không. ¾ EQL đầu tiên kiểm tra hai đối số có thỏa mãn EQ. Nếu không thỏa, nó sẽ xem chúng có cùng là số với cùng kiểu và giá trị. ¾ EQ kiểm tra hai đối số được lưu trữ cùng vùng nhớ trong máy tính hay không. ¾ = kiểm tra hai đối số biểu diễn cùng một số, ngay cả khi chúng không cùng kiểu. Lưu ý rằng các số được so sánh phải là cùng kiểu để thỏa mãn EQL. Nhưng với = thì có thể không cùng kiểu. * (eql 4 4.0) ; Các số khác kiểu NIL * (eql 4 4) ; Các số có cùng kiểu T Các số không cùng kiểu thỏa =: * (= 4 4.0) T Các đối số cho = phải có kiểu số. Bất kỳ một kiểu nào khác sẽ dẫn đến sinh ra lỗi. IV.3 Vị từ MEMBER Vị từ MEMBER kiểm tra xem đối số thứ nhất có phải là một phần tử của đối số thứ hai hay không. Lưu ý là MEMBER trả về những gì còn lại trong danh sách khi tìm thấy phần tử trùng hợp với đối số thứ nhất. * (setf sentence ‘(tell me more about your mother please)) (TELL ME MORE ABOUT YOUR MOTHER PLEASE) * (member ‘mother sentence) (MOTHER PLEASE) Một lưu ý nữa là MEMBER thông thường trả về NIL trừ khi đối số thứ nhất là một phần tử trực tiếp của đối số thứ hai; còn nếu chỉ là phần tử nằm trong một danh sách thì không đủ. * (setf pairs ‘((father son) (mother daughter))) (TELL ME MORE ABOUT YOUR MOTHER PLEASE) * (member ‘mother pairs) NIL Thay đổi cách xử lý các đối số Thông thường, MEMBER thực hiện việc kiểm tra với vị từ EQL – vị từ chỉ hoạt động với ký hiệu và số có cùng kiểu. Bây giờ chúng ta muốn MEMBER tìm một phần tử xuất hiện trong danh sách, chúng ta phải chỉ ra là chúng ta cần kiểm tra tính thành viên dùng vị từ EQUAL thay vì EQL. Hướng dẫn sử dụng Lisp 45 * (setf pairs ‘((mapple shade) (apple fruit))) ((MAPPLE SHADE) (APPLE FRUIT)) * (member ‘(mapple shade) pairs) NIL * (member ‘(mapple shade) pairs :test #’equal) ((MAPPLE SHADE) (APPLE FRUIT)) IV.4 Vị từ NULL và ENDP Ngoài vị từ LISTP, các vị từ quan trọng nhất trên danh sách là: Vị từ Mục đích null endp Đối số của vị từ là một danh sách rỗng ? Đối số của vị từ (phải là danh sách) là một danh sách rỗng ? Ví dụ: * (null ‘(this is not empty)) NIL * (endp ‘(this is not empty)) NIL * (null ‘()) T * (endp ‘()) T * (null ‘this-is-a-symbol) NIL * (endp ‘this-is-a-symbol) ERROR: ENDP: wrong type argument: THIS-IS-A-SYMBOL A CONS or NIL was expected Thông thường ENDP được dùng để kiểm tra danh sách rỗng, và NULL cũng vậy. Tuy nhiên ENDP dùng với một đối tượng không phải danh sách sẽ sinh ra lỗi. Vì vậy, nên dùng NULL cho việc kiểm tra dữ liệu nói chung có phải là NIL hay không, hơn là cho việc kiểm tra danh sách rỗng. IV.5 Các vị từ xác định kiểu dữ liệu Trong LISP, kiểu được gán cho dữ liệu chứ không phải cho biến. Ta có thể biết kiểu dữ liệu của biến nhờ vào các vị từ (prédicat) Vị từ Mục đích atom numberp symbolp Đối số có phải là một atom ? Đối số có phải là một số ? Đối số có phải là một ký hiệu ? Hướng dẫn sử dụng Lisp 46 stringp listp Đối số có phải là một chuỗi ? Đối số có phải là một danh sách ? Ví dụ: * (atom ‘pi) T * (atom pi) T * (numberp ‘pi) NIL * (numberp pi) T * (symbolp ‘pi) T * (symbolp pi) NIL * (listp ‘pi) NIL * (listp pi) NIL * (listp ‘(this is a list with pi in it)) T NIL tương đương với danh sách rỗng NIL và danh sách rỗng, ( ), hoàn toàn tương đương và thỏa mãn tất cả các vị từ EQ, EQL, và EQUAL. * (eq nil ‘()) T * (eql nil ‘()) T * (equal nil ‘()) T Cả NIL và danh sách rỗng đều được in là NIL. * nil NIL * () NIL Chính sự tương đương giữa NIL và () đã gây rắc rối, bởi vì NIL và () đều là ký hiệu và danh sách. Vì thế, cả (SYMBOLP ‘()) và (LISTP NIL) đều trả về T. * (atom nil) T * (atom ()) Hướng dẫn sử dụng Lisp 47 T * (symbolp nil) T * (symbolp ()) T * (listp nil) T * (listp ()) T IV.6 Các vị từ trên số Các vị từ trên số mang ý nghĩa khá rõ ràng: Vị từ Mục đích numberp zerop plusp minusp evenp oddp > < Đối số có phải là một số ? Đối số có phải là zero ? Đối số có phải là một số dương ? Đối số có phải là một số âm ? Đối số là số chẵn ? Đối số là số lẻ ? Các đối số theo thứ tự giảm dần ? Các đối số theo thứ tự tăng dần ? Ví dụ: * (setf zero 0 one 1 two 2 three 3 four 4) 4 * (setf digits (list zero on0e two three four)) (0 1 2 3 4) Dùng những giá trị trên, chúng ta có thể xem các vị từ số. NUMBERP kiểm tra xem đối số của nó có phải là một số hay không. * (numberp 4) T * (numberp four) T * (numberp ‘four) NIL * (numberp digits) NIL * (numberp ‘digits) NIL Hướng dẫn sử dụng Lisp 48 ZEROP nhận đối số là một số và kiểm tra nó có phải là zero hay không. ZEROP sẽ phát sinh lỗi nếu đối số không có kiểu số. * (zerop zero) T * (zerop ‘zero) ERROR: ZEROP: wrong type argument: ZERO A NUMBER was expected. * (zerop four) NIL PLUSP kiểm tra một số có phải là số dương hay không. * (plusp one) T * (plusp (- one)) NIL * (plusp zero) NIL EVENP kiểm tra một số có phải là số chẵn hay không. * (evenp two) T * (evenp (* 9 7 5 3 1)) NIL * (evenp (* 10 8 6 4 2)) T Các vị từ > và kiểm tra xem các đối số có giảm ngặt và < kiểm tra chúng có tăng ngặt hay không. Cả hai vị từ đều có thể nhận một, hai hay nhiều đối số. * (> four two) T * (>two four) NIL * (> three two one) T * (> three one two) NIL IV.7 Các toán tử logic 1. AND (and E1 E2 ... En) là sai nếu ít nhất một Ei sai AND đánh giá các đối số từ trái sang phải và chỉ dừng khi gặp một đối số sai Nếu mọi thông số đều đúng, AND trả về thông số cuối cùng Hướng dẫn sử dụng Lisp 49 Ví dụ: * (setq x ‘a) A *x A * (and (numberp x) (> x 1) ) NIL * (and (symbolp x) (list x) ) (A) 2. OR (or E1 E2 ... En) là sai nếu ít nhất một Ei sai OR đánh giá các đối số từ trái sang phải và chỉ dừng khi gặp một đối số đúng Ví dụ: * (setq x ‘a) A *x A * (or (numberp x) (> x 1) ) error > A is not a number * (or (symbolp x) (list x) ) T 3. NOT NOT đổi giá trị không NIL thành NIL và NIL thành T. Ví dụ: * (not nil) T * (not t) NIL * (not ‘dog) NIL * (setf pets ‘(dog cat)) (DOG CAT) * (member ‘dog pets) (DOG CAT) * (not (member ‘dog pets)) NIL * (member ‘dingo pets) NIL * (not (member ‘dingo pets)) Hướng dẫn sử dụng Lisp 50 T * (and (member ‘dog pets) (member ‘tiger pets)) NIL * (and (member ‘dog pets) (not (member ‘tiger pets))) T Chúng ta nhận thấy rằng NIL và danh sách rỗng – () là hoàn toàn tương đương nhau. Do đó NOT thật sự làm cùng một công việc như NULL: cả hai đều trả về T nếu đối số là NIL, và cả hai đều trả về NIL khi đối số là không NIL. IV.8 Các dạng điều kiện 1. IF, WHEN và UNLESS (if E1 E2 E3) Nếu E1 đúng, trả về giá trị E2 nếu không trả về giá trị E3 Ví dụ: * (if (numberp 1) ‘(a number) ‘(not a number)) (A NUMBER) * (if (numberp ‘a) ‘(a number) ‘(not a number)) (NOT A NUMBER) Ở đây, chỉ có một trong hai đối số – đối số thứ hai hoặc thứ ba được đánh giá. IF được gọi là có điều kiện vì IF phụ thuộc vào giá trị đối số của nó. Có nhiều dạng điều kiện khác phổ biến IF hơn khi người dùng không cần lắm tính tổng quát của IF. Chi tiết hơn, WHEN được dùng thay cho IF khi mệnh đề kết quả sai trong IF (biểu thức E3 ở trên) là NIL. (if E1 E2 nil) ≡ (when E1 E2) Tương tự như vậy, UNLESS được dùng thay cho IF khi mệnh đề kết quả đúng trong IF (biểu thức E2 ở trên) là NIL. (if E1 nil E3) ≡ (when E1 E3) Thật sự, cả WHEN và UNLESS có số lượng không giới hạn các đối số. Đối số thứ nhất là biểu thức cần kiểm tra; đối số cuối cùng trả về giá trị; và mọi đối số ở giữa được đánh giá cho các hiệu ứng lề. Trong ví dụ bên dưới, giá trị của WHEN là NEW-RECORD khi TEMPRATURE có giá trị lớn hơn HIGH. * (setf high 98 temprature 102) 102 * (when (> temprature high) ; So sánh TEMPRATURE với HIGH (setf high temprature) ; Nếu lớn hơn, thay đổi giá trị HIGH ‘new-record) ; Nếu lớn hơn, trả về NEW-RECORD Hướng dẫn sử dụng Lisp 51 2. COND (cond (Test1 E1 ) (Test2 E2 ) (Test3 E3 ) (Testn En ) ) ≡ (if Test1 (progn E1 ) (if Test2 (progn E2 ) (if Test3 (progn E3 ) (if Testn (progn En )) ) ) ) Trong một mệnh đề kiểu (Test1), nếu Test1 đúng, kết quả của cond là giá trị của (Test1) Ví dụ: Viết hàm trả về kiểu của đối số * (type-of 1) FIXNUM * (type-of a) SYMBOL Giải: (defun type-of (x) (cond ((null x) ‘null) ((symbolp x) ‘symbolp) ((numberp x) ‘numberp) ((stringp x) ‘stringp) ((consp x) ‘consp) (t ‘unknown-type) ) ) 3. CASE Các dạng IF, WHEN và UNLESS có thể xem như những trường hợp đặc biệt của COND vì tất cả đều có thể biểu diễn bằng COND. Chúng ta xem một trường hợp đặc biệt khác, CASE, có ý nghĩa tương tự như COND. CASE theo sau là dạng khóa (key form) và một dãy các mệnh đề. (case Key (Key1 E11 ) (Key2 E21 ) (Keyn En1 ) ) ≡ Hướng dẫn sử dụng Lisp 52 (cond ( (eql Key Key1) E11 ) ( (eql Key Key2) E21 ) ( (eql Key Key3) E31 ) ( (eql Key Keyn) En1 ) ) CASE dùng vị từ EQL và so sánh khóa được đánh giá với các khóa chưa được đánh giá. Nếu tìm thấy khóa, mệnh đề tương ứng sẽ được thực hiện và tất cả mọi biểu thức trong mệnh đề được đánh giá. Chúng ta dùng CASE khi ta muốn có COND trong đó mọi biểu thức kiểm tra tìm một giá trị trong nhiều khả năng. Trong ví dụ bên dưới, xét dạng COND sau: * (cond ((eq thing ‘circle) (* pi r r)) ((eq thing ‘sphere) (* 4 pi r r))) Ví dụ trên được viết lại dưới dạng CASE như sau. * (case thing ;ký hiệu THING sẽ được đánh giá (circle (* pi r r)) ;ký hiệu CIRCLE chưa được đánh giá (sphere (* 4 pi r r))) ;ký hiệu SPHERE chưa được đánh giá Hướng dẫn sử dụng Lisp 53 Chương V. Trừu tượng hóa dữ liệu Phần này sẽ giới thiệu khái niệm trừu tượng hóa dữ liệu – tiến trình giúp bạn xây dựng các chương trình lớn và phức tạp trong LISP. V.1 Các trường của một ký hiệu Ký hiệu là một đối tượng bao gồm nhiều trường: ¾ CVAL: giá trị của ký hiệu cũng như biến ¾ PNAME: chuỗi ký tự tương ứng với tên của ký hiệu (dùng cho máy in) ¾ FVAL: hàm gắn liền với ký hiệu, trường này không tồn tại trong LISP đơn trị LISP đa trị (bi-valued) LISP đơn trị (mono-valued) * (setq + 4) 4 * (+ + 3) 7 * (setq + *) var + indef * (setq + 4) 4 *

Các file đính kèm theo tài liệu này:

  • pdfgiao_trinh_thuc_hanh_ngon_ngu_lap_trinh.pdf
Tài liệu liên quan