Đề cương môn học THNNLT (First draft) PHẦN I: PROLOG
Sinh viên cần nắm các nội dung sau: Cách biểu diễn một danh sách. Các thao tác trên danh sách ... quan hệ last(Item, List) biểu diễn quan hệ Item là phần tử ...
Đề cương môn học THNNLT
(First draft)
PHẦN I: PROLOG
Tiết 1: GIỚI THIỆU NGÔN NGỮ LẬP TRÌNH PROLOG (lý thuyết)
Tiết 2: Giới thiệu tổng quan về PROLOG
Sinh viên cần nắm được các nội dung sau:
• Môi trường làm việc của PROLOG
• Cách định nghĩa vấn đề trong PROLOG: vị từ
• Cách biểu diễn vấn đề trong PROLOG: các sự kiện, luật
• Các chương trình ví dụ
• Cách đặt câu hỏi và nhận câu trả lời trong PROLOG
• Phân biệt Goal nội và goal ngoại
Bài tập / bài thực hành:
1/ Làm quen với môi trường làm việc của PROLOG 2.0
2/Định nghĩa một số khái niệm, sự kiện và luật trong PROLOG:
a/Socrates là người. Socrates là người Hy Lạp. Aristottle là người. Xeda là
người. Xeda là vua. John là vua của nước Pháp.
b/John likes Mary. Mary likes John. Joe likes fish. Joe likes Mary. Mary likes
book. John likes book. Mary is female.
c/ Xây dựng quan hệ gia đình(parent, child, grandparent, aunt) theo sơ đồ sau:
-
pam tom +
Ghi chú:
+:nam
-:nữ + -
bob liz
- +
ann pat
+
jim
d/Nếu có mưa thì tôi sẽ sử dụng dù. Nếu X là người thì X sẽ chết. X là chim
nếu X là sinh vật(aninal) và X có lông vũ. X là chị(em gái) của Y nếu X là phụ
nữ và X và Y có cùng cha mẹ. X là ông của Y nếu cha của Y là Z và cha của Z
là X (X, Z đều là nam)
3/Đặt câu hỏi và nhận câu trả lời.
a/Socrates có phải là người Hy Lạp không? Aristotle có phải là người Hy Lạp
không? Socrates có phải là người Việt Nam không? Ai chết? Ai là vua?
Socrates có chết không?
b/Does John like Mary? What does John like? Is there an object that likes
Mary? Is there anything that John and Mary both like?
c/ Ai là cha của Pat? Mẹ của Bob là ai? Ai là ông của Pat? Ai là chị(em gái)
của Pat? Ai là con của Tom? atom
Tiết 3: Cú pháp và ngữ nghĩa của các chương trình PROLOG
Sinh viên cần nắm các nội dung sau:
• Các đối tượng dữ liệu: atom, numbers, variables, structes
• Phép so trùng(matching)
• Ý nghĩa của các khai báo trong Prolog
• Prolog trả lời các câu hỏi như thế nào
Bài tập / bài thực hành:
1/Trong những đối tượng sau, đối tượng nào đúng theo ngữ pháp của Prolog? Chúng
thuộc loại đối tượng gì (cơ bản, số, biến, cấu trúc)?
Diana, diana, ’Diana”, _diana, “Diana goes South”, goes(diana, south), 45, 5(X,Y),
+(north, west), three( Black(Cats)
2/Trong các phép so trùng(matching) sau, phép so sánh nào thành công(succeed) và
biến nào(nếu có) sẽ hợp nhất với giá trị nào?
pilots(A,london) = pilots(london,paris)
point(X,Y,Z) = point (X1,Y1,Z1)
letter(C) = word(letter)
‘vicar’ = vicar
point(A,B) = point(1,2)
point(A,B) = point(X,Y,Z)
plus(2,2) = 4
+(2,D) = +(E,2)
3/Xây dựng cấu trúc dữ liệu cho các đối tượng hình tam giác, chữ nhật, hình vuông,
hình tròn
4/Giả sử cấu trúc dữ liệu của một tứ giác là rectangle(P1,P2,P3,P4) với P là các đỉnh
của tứ giác. Hãy định nghĩa quan hệ
regular(R) cho kết quả là true nếu R là tứ giác có các cạnh thẳng đứng song song với
trục tung và nằm ngang đều song song với trục hoành.
5/Cho chương trình sau:
f(1,one).
f(s(1),two).
f(s(s(1)),three).
f(s(s(s(X))), N) :- f(X,N).
Kết quả các câu hỏi sau là gì? Nếu có nhiều câu trả lời thì hãy liệt kê ít nhất là 2 câu
a/f(s(1),A).
b/f(s(s(1)), two).
c/f(s(s(s(s(s(s(1)))))),C).
d/f(D,three).
6/Chương trình sau đây cho rằng hai người bất kỳ là bà con(relatives) với nhau nếu:
• người này là tiền nhân(predecessor) của người kia, hay
• cả hai người đều có cùng tiền nhân, hay
• cả hai người đều có cùng hậu nhân(successor)
relatives(X,Y) :- predecessor(X,Y).
relatives(X,Y) :- predecessor(Y,X).
relatives(X,Y) :- predecessor(Z,Y), predecessor(Z,X).
relatives(X,Y) :- predecessor(X,Z), predecessor(Y,Z).
Hãy rút gọn chương trình này.
7/Viết lại chương trình sau mà không sử dụng các dấu chấm phẩy(semicolon)
translate(Number, Word) :-
Number = 1, Word = one;
Number = 2, Word = two;
Number = 3, Word = three.
Tiết 4: Cấu trúc dữ liệu, danh sách và các phép toán số học
Sinh viên cần nắm các nội dung sau:
• Cách biểu diễn một danh sách
• Các thao tác trên danh sách
• Các phép toán số học(arithmetic)
Bài tập / bài thực hành:
1/Viết vị từ conc để nối hai danh sách.
2/
a/Dùng vị từ conc đã viết ở câu 1, viết 1 goal để xóa 3 phần tử cuối của danh sách L
b/Viết một dãy các goal để xóa 3 phần tử đầu tiên và 3 phần tử cuối cùng của danh
sách L
3/Định nghĩa quan hệ last(Item, List) biểu diễn quan hệ Item là phần tử cuối của
danh sách L bằng 2 cách: dùng conc và không dùng conc.
4/Viết 2 vị từ:
evenlength(List) và oddlength(List)
trả về true nếu thông số của chúng là 1 danh sách có chiều dài là một số chẳn(lẻ)
tương ứng.
VD: [a,b,c,d] là evenlength còn [a,b,c] là oddlength
5/Định nghĩa quan hệ reverse(List, ReverseList) dùng để đảo ngược danh sách List
thành danh sách ReverseList
VD: reverse([a,b,c,d].[d,c,b,a])
6/Viết vị từ palindrome(List) trả về true nếu List là một danh sách đối xứng
7/ Định nghĩa quan hệ shift(L1,L2) để dịch chuyển 1 phần tử trong L1 vòng sang trái
tạo thành L2
Ví dụ: shift([1,2,3,4],L1) -> L1=[2,3,4,1]
8/Định nghĩa quan hệ translate(L1,L2) để chuyển đổi danh sách số nguyên(từ 0 đến
9) L1 thành danh sách các từ tương ứng L2.
Ví dụ: translate([3,5,1,3],[ba,nam,mot,ba])
9/Định nghĩa quan hê subset(Set,Subset) với Set và Subset là 2 tập hợp để kiểm tra
Subset có là tập con của Set không cũng như để tìm các tập con có thể có của Set.
Ví dụ: subset([a,b,c], X)
X = [a,b,c]
X = [b,c]
X = [c]
X = []
X = [a,c]…
10/Hãy định nghĩa quan hệ dividelist(List, List1, List2) để chia danh sách List thành
hai danh sách con List1 và List2 sao cho chiều dài của 2 danh sách con này xấp xỉ
bằng nhau.
Ví dụ: devidelist([a,b,c,d,e],[a,c,e],[b,d])
11/Viết vị từ max(X,Y,Max) sao cho Max là số lớn nhất trong hai số X và Y
12/Viết vị từ maxlist(List, Max) để tính số lớn nhất Max trong danh sách List
13/ Viết vị từ sumlist(List, Sum) để tính tổng của một danh sách
14/ Viết vị từ ordered(List) trả về true nếu danh sách List có thứ tự (tăng hay giảm)
Ví dụ:
ordered([1,5,6,6,9]) ->true
15/Viết vị từ subsum(Set, Sum, Subset) với Set là một danh sách các số nguyên,
Subset là tập con của Set, và Sum là tổng các số trong Subset
Ví dụ:
subsum([1,2,5,3,2],5,Sub) -> Sub = [1,2,2]; Sub = [2,3]; Sub = [5]…
Tiết 5: Sự đệ qui, cơ chế Backtracking và kỹ thuật khống chế số lượng lời giải
Sinh viên cần nắm các nội dung sau:
• Nắm vững và vận dụng cách suy nghĩ đệ qui để giải quyết các bài toán trên
Prolog
• Hiểu,nắm vững và điều khiển được cơ chế quay lui (backtraking) của Prolog
• Hiểu và sử dụng tốt vị từ fail và nhát cắt (!) để khống chế số lượng lời giải
Bài tập / bài thực hành:
1/ Viết vị từ nthmember(N, List, X) trả về true nếu X là phần tử thứ N trong danh
sách List.
2/ Cho chương trình sau:
p(1).
p(2):-!.
p(3).
Hãy viết tất cả các câu trả lời của Prolog khi đặt các câu hỏi sau:
a/p(X).
b/p(X),p(Y).
c/p(X),!,p(Y).
3/Quan hệ sau sẽ xác định một số nguyên(Number) nhập vào sẽ là số dương(positive),
âm(negative) hay là số không(zero):
class(Number, positive) :- Number > 0.
class(0, zero).
class(Number, negative) :- Number < 0.
Hãy sử dụng (các) nhát cắt để làm tăng tính hiệu quả của quan hệ này.
4/ Viết vị từ split(Numbers, Positives, Negatives) để tách một danh sách số nguyên
Numbers thành 2 danh sách: Positives (chứa các số >=0) và Negatives (chứa các số <
0). Hãy viết thành 2 phiên bản: có và không sử dụng nhát cắt.
5/ Hãy định nghĩa quan hệ hiệu của hai tập hợp:
difference(Set1,Set2,SetDifference)
Tất cả các tập hợp sẽ được biểu diễn dưới dạng danh sách
Ví dụ:
difference([a,b,c,d], [b,d,e,f], [a,c]).
Tài liệu tham khảo:
1/Ivan Bratko (1986) PROLOG Programming for Artificial Intelligent. Addison-
Wesley
2/W.F Clocksin and C.S.Mellish (1981) Programming in Prolog. Springer-Verlag.
3/Turbo Prolog 2.0 userguide
4/Hướng dẫn sử dụng PROLOG
PHẦN II: SMALLTALK
Tiết 6: Giới thiệu ngôn ngữ lập trình SMALLTALK(lý thuyết)
Tiết 7: Làm quen với Smalltalk
Sinh viên cần nắm các nội dung sau:
• Môi trường làm việc của Smalltalk 2000 và Smalltalk/V
• Viết được một số đoạn script ví dụ đơn giản
Bài tập / bài thực hành:
1/ Làm quen với cách sử dụng cửa sổ duyệt cây phân cấp các lớp (Class Hirerachy
Browser)
2/ Làm quen với cách sử dụng Disk Browser và các browser khác
3/ Làm quen với cách sử dụng Inspector, Workspaces, Debugger
4/ Làm quen với cách import, export (save, load) một Class hoặc một method
5/Tìm hiểu các chức năng khác trong hệ thống menu
6/Làm quen với môi trường soạn thảo code(editor) trong smalltalk
6/ Hãy cho biết lớp Integer được thừa kế từ các lớp nào trong cây phân cấp lớp của
Smalltalk. Các lớp nào khác có cùng cha với lớp Integer
7/Thực thi đoạn script biểu thức sau trên cả hai phiên bản của Smalltalk:
(3 + 6) < (6 * 2)
8/Thực thi tất cả các ví dụ có trong tài liệu lý thuyết trong cả hai môi trường
Smalltalk/V và Smalltalk 2000.
Tiết 8: Đối tượng(Object) và thông điệp(Messages)
Sinh viên cần nắm các nội dung sau:
• Các đối tượng đơn giản
• Các thông điệp đơn giản
• thông điệp unary, keyword, binary (bao gồm cả các thông điệp toán học)
• thông điệp lồng nhau
• Các biến tạm và biến toàn cục
• Các biểu thức
• Cách ghi chú
Bài tập / bài thực hành:
1/Hãy biểu diễn biểu thức 5+2-3/(2*3 - 5) và cho biết kết quả tính toán của smalltalk
với biểu thức này
2/Hãy tính căn bậc hai của biểu thức trên
4/Hãy tính bình phương của biểu thức trên
5/Trong smalltalk có phép chia nguyên và phép chia dư không? Nếu có, chúng có tên
là gì? thuộc lớp nào?
6/Làm thế nào để sử dụng các phép logic(and, or, not…) trong smalltalk. Hãy biểu
diễn biểu thức: (delta >= 0) and (a 0) trong smalltalk
7/Làm thế nào để khai báo một đối tượng thuộc một lớp nào đó trong smalltalk. Hãy
tạo mới một đối tượng a thuộc lớp Array có 4 phần tử.
8/Hãy cho biết đối tượng #(1 (‘two ‘three’) 4) là đối tượng thuộc lớp gì trong
Smalltalk
9/Hãy cho biết kết quả sau khi Smalltalk thực hiện thông điệp size cho đối tượng trên
10/Thông điệp size trên thuộc loại thông điệp nào(unary, keyword,binary,arithmetic)?
11/Hãy tìm và thử một số thông điệp khác mà một đối tượng thuộc lớp này có thể đáp
ứng.
12/Cho một dãy gồm các số và ký tự bất kỳ trong smalltalk. Hãy xác định dãy các
phần tử là số trong dãy này.
Ví dụ: với dãy (1 $a 2 $b) -> (1 2)
13/Cho một dãy gồm ký tự bất kỳ trong smalltalk. Hãy xác định dãy các phần tử
nguyên âm trong dãy này.
Ví dụ: với dãy ($a $a $b $c $e) -> ($a $a $e)
Tiết 9: Các cấu trúc điều khiển (Control Structure)
Sinh viên cần nắm các nội dung sau:
• So sánh hai đối tượng
• Phát biểu điều kiện
• Biểu thức logic
• Các cấu trúc lặp
• Cấu trúc khối với các thông số
Bài tập / bài thực hành:
1/ Viết một phương thức uocso có đối số là một số nguyên b, đối tượng nhận thông
điệp là b cho class Integer để kiểm tra xem b có là ước số của a hay không.
2/ Viết một phương thức boiso có đối số là một số nguyên b, đối tượng nhận thông
điệp là b cho class Integer để kiểm tra xem b có là bội số của a hay không.
3/ Viết cho lớp integer phương thức songuyento để xác định xem đối tượng nhận
thông điệp có là số nguyên tố hay không.
4/ Viết phương thức giaithua cho lớp Integer để tính giai thừa cho một số nguyên n.
5/ Viết phương thức tìm ước số chung lớn nhất của hai số nguyên a và b
6/ Viết chương trình tìm bội số chung nhỏ nhất của hai số nguyên a và b
7/ Viết phương thức tính số hạng thứ n của dãy Fibonaci.
8/ Viết phương thức dem cho lớp String để đếm số ký tự là chữ hoa trong đối tượng
nhận thông điệp.
9/ Viết phương thức tach cho lớp String để tạo ra chuỗi s1 là những ký tự trong s có
mã ASCII chia hết cho vị trí của ký tự đó trong s(đối tượng nhận thông điệp).
10/ Viết phương thức cộng để cộng hai vector
11/ Viết phương thức tich để tính tích vô hướng của hai vector.
12/ Viết phương thức sort để sắp xếp thứ tự của một dãy.
13/ Viết chương trình(script) giaiptbac1 có 2 đối số là a, b(có xét trường hợp a =0)
14/ Viết chương trình giải phương trình bậc hai
15/ Viết chương trình tính diện tích tam giác khi biết ba cạnh
16/ Viết chương trình tính diện tích và chu vi hình tròn khi biết bán kính.
17/ Viết phương thức để tính định thức của ma trận vuông
Tiết 10: Các lớp(Classes) và phương thức(Methods) _ Tính thừa kế(inheritance) và
tính đa hình(Polymorphism)
Sinh viên cần nắm các nội dung sau:
• Khái niệm lớp và phương thức
• Cách duyệt cây phân cấp lớp
• Biến “self”, đối tượng “nil”
• Tạo đối tượng mới
• Biến thực thể tên và biến thực thể chỉ số(index instance variables), biến lớp
• thêm phương thức mới, phương thức lớp
• Sự thừa kế (cả về phương thức lẫn biến)
• Tính đa hình
• So trùng mẫu (pattern matching)
Bài tập / bài thực hành:
1/ Hãy định nghĩa lớp vehicles có các thuộc tính: X,Y(position), direction và các
phương thức: forward, back, turnleft, turnright.
2/ Hãy định nghĩa lớp tank là lớp con của lớp vehicles có thêm các thuộc tính: weapon,
speed, state, attack_range, damage và các phương thức: fire, attack, retreat, guard
3/ Hãy định nghĩa lớp harvester là lớp con của lớp vehicles có thêm các thuộc tính:
state, capacity, speed và các phương thức: harvest
4/ Hãy so sánh giữa lớp(Classes) và kiểu dữ liệu trừu tượng(abstract data types)
5/ Giả sử có 3 đối tượng A, B, C trong cùng một chương trình lớn đều sử dụng đối
tượng R để cung cấp các số ngẫu nhiên(random number). Chúng ta nên sử dụng các
bản sao khác nhau của R cho từng đối tượng A, B, C hay dùng chung(share) cùng một
đối tượng R cho cả ba đối tượng A, B, C? Tại sao?
Ghi chú: Sinh viên làm thêm bài tập trong tài liệu lý thuyết
First Project in Smalltalk/V: A Prioritizer
1. Giới thiệu
Trong dự án đầu tiên này, chúng ta sẽ thiết kế và xây dựng một ứng dụng nhỏ,
làm cho nó có thể truy xuất được từ System menu và Demo menu rồi chạy thử
nó. Trong quá trình xây dựng dự án này, chúng ta sẽ làm quen với cách viết
code trong môi trường Smalltalk/V cũng như hiểu rõ thêm về khả năng của bộ
Debugger trong Smalltalk/V.
Chúng ta sẽ sử dụng các lớp sau trong dự án này:
1.1. SortedCollection
1.2. Prompter
1.3. DemoClass
1.4. ScreenDispatcher
Ngoài ra chúng ta còn tạo thêm một lớp mới là Prioritizer
2. Project Overview
Dự án mà chúng ta sẽ xây dựng là một chương trình nhỏ cho phép
người sử dụng nhập vào một danh sách các item theo thứ tự bất kỳ, sau đó
chương trình sẽ giúp ta sắp xếp lại các item này theo mức độ quan trọng từ cao
đến thấp hay ngược lại. Nói cách khác, thay vì sử dụng các phép so sánh toán
học “lớn hơn”, “nhỏ hơn” cổ điển có sẵn trong một số lớp của Smalltalk/V, dự
án Prioritizer sẽ sử dụng các phép so sánh do người dùng định nghĩa. Điều này Comment: Xem lại câu văn của
đoạn này
cũng giống như trong thực tế, chúng ta thường phải ra nhiều quyết định khác
nhau trong cuộc sống hằng ngày. Ứng dụng Prioritizer này sẽ hỗ trợ chúng ta
ra quyết định giữa hai hay nhiều chọn lựa khác nhau bằng cách xem xét đến
từng giá trị có thể có của mỗi lựa chọn khi so sánh chúng với nhau.
3. Thiết kế
Xét ở mức độ đơn giản nhất thì dự án này sẽ chỉ có nhiệm vụ chính là
chuyển đổi một thực thể của lớp Collection thành một thực thể của lớp
SortedCollection. Chúng ta sẽ thêm vào một số ràng buộc khác vào thiết kế
này để đáp ứng được yêu cầu là ứng dụng hoàn chỉnh phải có nhiều thành
phần khác nhau. Cụ thể là chúng ta sẽ phải xây dựng các thành phần để:
• cho phép người sử dụng nhập các item cần được sắp xếp độ ưu
tiên
• yêu cầu người sử dụng xếp loại (rank) các cặp trong danh sách
các item và trả lời câu hỏi ứng với mỗi cặp phần tử: “phần tử
này lớn hơn hay nhỏ hơn phần tử kia?”
• hiển thị danh sách kết quả đã được sắp xếp.
• “gắn” (làm cho có thể truy xuất được) ứng dụng vào trong
System menu và Demo menu
4. Xây dựng dự án
Chúng ta sẽ bắt đầu việc xây dựng dự án này bằng cách xây dựng một
số thành phần cơ bản của nó trong workspace và kiểm tra chúng. Một khi đã
xây dựng thành công những thành phần này thì chúng ta có thể copy và paste
chúng thành method Prioritize.
Chúng ta sẽ đi theo các bước đã thiết kế ở phần trên, do đó đầu tiên
chúng ta sẽ xây dựng một đoạn chương trình để yêu cầu người sử dụng nhập
vào một danh sách các phần tử(item) cần sắp xếp. Chúng ta sẽ cần một cách
nào đó để hỏi người sử dụng, nhận các thông tin đáp ứng từ phía họ và đưa các
thông tin này vào danh sách các phần tử để sau này chúng ta sẽ tiến hành sắp
thứ tự theo hướng dẫn của người sử dụng.
Ở trường hợp này, các lớp mà chúng ta sẽ sử dụng xuất phát (devire)
một cách rất tự nhiên từ các mô tả chương trình đã nêu ở phần trên. (Tuy nhiên,
cách tiếp cận này còn mang khá nặng tính thủ tục(procedural approach), không
phải là cách tốt nhất để thiết kế một dự án bằng Smalltalk. Trong phần sau Comment: Chưa làm tới
chúng ta sẽ có cách tiếp cận để thiết kế dự án hướng đối tượng hơn.) Chúng ta
cần tìm các lớp trong Smalltalk/V cho phép chúng ta đưa ra câu hỏi cho người
sử dụng và nhận đáp ứng từ họ. Sau khi tìm kiếm trong các lớp của Smalltalk
(có thể tìm thấy trong các tài liệu đi kèm theo phần mềm) chúng ta thấy là lớp
Prompter sẽ đáp ứng được các yêu cầu tương tác người dùng này.
4.1. Lớp Prompter
Trong Smalltalk/V Prompter là một cửa sổ nhỏ với một khung soạn
thảo(TextPane) dùng để đưa ra một câu hỏi cho người sử dụng ở dòng đầu
tiên(header) và cho phép người sử dụng nhập câu trả lời ở phần bên dưới. Comment: Chèn một cái hình
vào đây
Trong tài liệu đi kèm(manual) smalltalk chúng ta sẽ dễ dàng tìm thấy 3
methods của lớp này dùng cho việc đối thoại với người sử dụng:
• prompt:default:
• prompt:defaultExpression:
• promptWithBlanks:default:
Do chúng ta không cần phải tính toán, đánh giá các câu trả lời do
người sử dụng nhập vào nên ta loại phương thức thứ hai ra khỏi danh sách các
phương thức sẽ sử dụng.
Phương thức đầu tiên và phương thức thứ ba có tác dụng gần giống
nhau. Tuy nhiên phương thức promptWithBlanks:default: quan tâm đến các
khoảng trắng(space) mà người sử dụng vô tình nhập vào cửa sổ prompter. Do
việc này không cần thiết đối với dự án này nên chúng ta sẽ sử dụng phương
thức đơn giản hơn là prompt:default:.
Tóm lại, bây giờ chúng ta sẽ xử lý quá trình giao tiếp với người sử
dụng thông qua một thực thể của lớp Prompter mà cụ thể là ta sẽ sử dụng
phương thức prompt:default: của lớp này để nhận vào danh sách các items từ
phía người sử dụng để sắp xếp.
4.2. Tạo một Prompter
Hãy mở cửa sổ Workspace trong môi trường Smalltalk/V và nhập đoạn
code sau đây:
|anotherItem|
anotherItem := Prompter prompt:
‘Something to prioritize? (or leave blank)’
default: ‘’.
^anotherItem
Sau đó, chọn(highlight) đoạn mã này và chọn show it từ ô menus. Kết
quả sẽ giống như trong hình
Khi người sử dụng nhập vào một đoạn text nào đó và nhấn Enter,
Smalltalk/V sẽ hiển thị lại đoạn text này trong cửa sổ Workspace. Bây giờ,
chúng ta hãy tìm hiểu cụ thể xem đoạn mã ở trên làm việc ra sao.
Dòng đầu tiên của đoạn mã chỉ đơn giản là khai báo một biến cục bộ
có tên là anotherItem dùng để lưu các giá trị do người sử dụng nhập vào.
Chúng ta chọn tên này vì chúng ta hiểu rằng ứng dụng hoàn chỉnh sau cùng sẽ
cho phép người sử dụng nhập vào nhiều hơn 1 item.
Dòng kế sử dụng phương thức prompt:default: của lớp Prompter như
đã được trình bày ở trên để đặt câu hỏi vào trong cửa sổ prompter và phần text
mặc định là trống.
Dòng cuối cùng của đoạn mã trên trả về giá trị mà người sử dụng đã
nhập vào Prompter.
Tuy nhiên, trả về giá trị này là điều không cần thiết trong ứng dụng của
chúng ta. Chúng ta cần phải tạo một đối tượng mới để lưu các giá trị
này(entry). Vậy đối tượng này sẽ thuộc loại nào? Đối tượng này phải có khả
năng lưu trữ nhiều hơn 1 đối tượng tại một thời điểm nào đó. Do đó, nó có thể
là một loại Collection nào đó. Lớp Collection này có ba lớp con là: Bag,
IndexedCollection và Set. Do chúng ta không quan tâm đến thứ tự của các
thành phần(elements) trong giai đoạn nhập thông tin từ người sử dụng nên
chúng ta không cần đến lớp IndexedCollection. Bag có thể có phần tử trùng
nhau mà Set thì không có. Trong trường hợp này, nếu người sử dụng nhập vào
một giá trị đã có vào danh sách các lựa chọn để sắp thứ tự thì giá trị này sẽ
được bỏ qua. Do đó chúng ta cần phải chắc chắn rằng danh sách kết quả sẽ chỉ
có 1 điểm nhập(entry) cho mỗi lựa chọn mà người sử dụng muốn sắp xếp. Vì
vậy chúng ta sẽ sử dụng thực thể của lớp Set.
Nếu chúng ta đơn giản chỉ định nghĩa một biến cục bộ mới để giữ danh
sách tích lũy của các đối tượng và sau đó sửa lại danh sách đã nhập trước đó
để thêm vào tập hợp này, chúng ta sẽ gặp phải một vấn đề là làm sao để dừng
quá trình này? Chúng ta cần một phương pháp để cho phép người sử dụng chỉ
ra rằng họ vừa mới nhập xong phần tử cuối cùng. Chúng ta đã chọn là: khi
người sử dụng không nhập thông tin gì mà nhấn enter thì xem như là danh
sách nhập đã chấm dứt. Sau đây chúng ta sẽ xây dựng đoạn mã để xử lý tình
huống này. Nếu thông tin người sử dụng nhập vào là trống thì quá trình nhập
kết thúc, ngược lại, chúng ta sẽ tiếp tục đưa ra các Promter mới để yêu cầu
người sử dụng nhập tiếp.
Sau đây là đoạn mã hoàn chỉnh dùng để nhập cũng như hiển thị tập
hợp các lựa chọn được đưa vào bởi người sử dụng:
|items anotherItem|
itém := Set new.
[(anotherItem := Prompter prompt:
‘Something to prioritize? (or leave blank)’
default: ‘’) = ‘’] whileFalse: [items add: anotherItemư.
^items.
Chọn toàn bộ đoạn mã này rồi chọn mục show it từ hệ thống menu.
Kết quả xuất hiện trên màn hình sẽ giống như trong hình bên dưới sau khi Comment: Copy 1 cái hình vào
đây
nhập vào một số phần tử và nhấn enter sau khi đã nhập hết các phần tử. Chú ý
rằng Smalltalk thật sự sẽ trả về một tập hợp. Chúng ta có thể nhập vào các
phần tử trùng nhau để kiểm tra tính duy nhất của dữ liệu nhập như đã trình bày
ở phần trên hay không.
4.3. Sắp xếp danh sách của người sử dụng
Do chúng ta sẽ có một danh sách các đối tượng để sắp xếp nên chúng
ta sẽ có cơ hội để làm việc thêm với một trong các lớp con của lớp Collection.
Tra cứu trong thư viện các lớp của Smalltalk chúng ta thấy có lớp
SortedCollection có vẻ như là sẽ đáp ứng được yêu cầu sắp xếp của chúng ta.
Điều này được khẳng định là đúng khi chúng ta đọc qua phần mô tả
của lớp này. Chú ý rằng bất kỳ thực thể nào thuộc lớp này đều có thể truy xuất
vào một đoạn mã có tên là sortBlock. Đoạn mã này có hai đối số và trả về một
giá trị kiểu luận lý là true nếu đối số thứ nhất được sắp cao hơn theo thứ tự
được định nghĩa bởi đoạn mã so với đối số thứ hai, ngược lại thì trả về false.
Smalltalk/V đã định nghĩa sẵn(default) khối này theo thứ tự alphabet.
Do phép so sánh x > y chính là hành vi mà chúng ta mong muốn trong
ứng dụng Prioritizer nên việc lựa chọn lớp nào thích hợp đã quá rõ ràng,
không cần phải tìm thêm các lớp khác nữa. Chúng ta chỉ cần sử dụng khía
cạnh này của lớp SortedCollection trong dự án của chúng ta là đủ. Tuy nhiên,
sau này chúng ta sẽ thấy các ứng dụng mở rộng khác của lớp này cũng như các
phương thức mạnh mẽ của chúng.
Một điều thú vị ở đây là có thể chúng ta sẽ nghĩ là tại sao chúng ta lại
không đơn giản thêm phương thức prioritize vào cho lớp SortedCollection mà
lại phải tạo riêng trong các menu như đã trình bày ở trên? Nếu chúng ta làm
như vậy thì sẽ không hiệu quả bởi vì các phần tử sẽ được sắp xếp mỗi khi
chúng được thêm vào trong một thực thể của lớp SortedCollection(xem mô tả
của lớp này). Điều này sẽ dẫn đến việc là tất cả các phần tử sẽ được sắp xếp lại
hoàn toàn mỗi khi thêm một phần tử vào trong danh sách và trong quá trình
thực thi phương thức prioritize. Người sử dụng sẽ bị yêu cầu so sánh đi so
sánh lại các phần tử nhiều lần. Việc này rõ ràng là tốn thời gian vô ích.
Chúng ta sẽ thêm khả năng sắp xếp vào trong chương trình của chúng
ta. Để làm được việc này chúng ta cần phải định nghĩa lớp sortBlock để xác
định các phần tử trong danh sách sẽ được sắp xếp theo cách nào. Đoạn mã này
sẽ thực thi trên từng cặp phần tử trong tập hợp được nhập vào gọi là items và
trả về một thực thể của lớp SortedCollection gọi là result.
Chúng ta sẽ sử dụng một phương thức khác của lớp Prompter trong
đoạn mã sortBlock này. Đó là phương thức prompt:defaultExpression:.
Phương thức này sẽ đánh giá các đáp ứng của người sử dụng trước thay vì chỉ
đơn giản trả về các chuỗi thông tin đã nhập của người sử dụng. Do sortBlock
phải trả về một giá trị luận lý nên chúng ta muốn Smalltalk phải chuyển các
thông tin nhập vào của người sử dụng thành giá trị luận lý.
Trong cửa sổ Workspace của chúng ta trước đây, hãy xóa dòng trả về
giá trị của items(dòng cuối cùng), và thêm một biến mới tên là result vào danh
sách các biến ở đầu chương trình. Bây giờ, hãy thêm đoạn mã sau đây vào sau
khối của whileFalse
result := SortedCollection sortBlock:
[:a :b | Prompter prompt:
‘Does ‘, a printString,
‘have higher priority than’,
b printString,’ ? (Enter true or false) ‘
defaultExpression: ‘false’].
result addAll: items.
^result.
Chọn chương trình rồi show it. Nhập vào hai hay ba item để sắp xếp
rồi nhấn Enter để báo hiệu là đã kết thúc quá trình nhập thông tin. Bây giờ, khi
chúng ta được hỏi item này có lớn hơn item kia hay không(xem hình) chúng ta Comment: Thêm 1 cái hình vào
đây
có thể nhấn Enter để trả lời là false(default) hoặc chọn câu trả lời mặc định rồi
gõ lại là true.(Lưu ý là phải sử dụng chữ thường). Khi chúng ta đã cung cấp
câu trả lời cho tất cả các cặp phần tử mà chúng ta đã nhập thì Smalltalk/V sẽ
trả về giá trị của biến result đã được sắp thứ tự theo yêu cầu của chúng ta.
Ngôn ngữ mà chúng ta đã sử dụng trong prompter ở trên có vẻ không
được tự nhiên lắm. Chúng ta muốn là có thể trả lời là “Yes” hay “No” cho câu
hỏi để so sánh thay vì trả lời là “true” và “false” nhưng khối sortBlock đòi hỏi
kiểu kết quả là Boolean mà trong Smalltalk/V, kiểu Boolean chỉ mang 2 giá trị
là true và false. Chúng ta có thể cho phép người sử dụng nhập vào câu trả lời
kiểu khác rồi chuyển chúng sang dạng Boolean nhưng điều này làm chúng ta
phải viết thêm vài dòng code nữa. Tuy nhiên, chúng ta cũng sẽ không làm do
sau này chúng ta sẽ thay đổi giao diện với người sử dụng(click chuột chẳng
hạn) trong việc trả lời cho chương trình.
4.4. Hiển thị kết quả
Có rất nhiều nơi chúng ta có thể hiển thị kết quả của một chương trình
trong Smalltalk/V. Có lẽ nơi thông thường nhất, đặc biệt là các ứng dụng đơn
giản như ứng dụng Prioritize này là cửa sổ Transcript. Chúng ta đặt các ký
hiệu và các đoạn text vào hệ thống cửa sổ Transcript bằng cách gửi các chuỗi
đến một biến hệ thống toàn cục là Transcript. Chúng ta quy ước là sẽ sử dụng
từ: kỹ thuật message cascading khi gửi một loạt các thông điệp đến cùng một
đối tượng(như Transcript trong trường hợp này).
Nhìn lại đoạn mã đã viết, chúng ta thấy biến result chứa một thực thể
của lớp SortedCollection sau khi chúng ta hoàn tất quá trình sắp xếp dưới sự
trợ giúp của người sử dụng. Bây giờ có một câu hỏi đặt ra là: làm thế nào để
hiển thị một đối tượng kiểu này? Do Transcript là một thực thể của lớp
TextEditor nên chúng ta sẽ tìm trong tài liệu về các lớp của Smalltalk/V để
biết cách để hiển thị các phần tử trong một thực thể của lớp này. Chúng ta sẽ
tìm thấy trong đó có phương thức tên là nextPuttAllL: có vẻ như là để dùng
vào việc này. Dù vậy chúng ta cũng nên chú ý rằng phương thức này đòi hỏi
thông số truyền vào phải là một chuỗi. Chúng ta đã định nghĩa biến result là
một thực thể của lớp SortedCollection vậy thì câu hỏi tiếp theo là làm thế nào
để biến nó thành một chuỗi.
Điều này có vẻ dễ giải quyết nhưng chúng ta sẽ thấy là không thể tìm
được giải pháp cho vấn đề khi duyệt qua thư viện các lớp của Smalltalk như đã
từng làm trước đây. Mọi phần tử của lớp Object đều biết cách đáp ứng với
phương thức hệ thống printString. Phương thức này được định nghĩa trong lớp
Object; nó sẽ trả về một chuỗi dạng ASCII của đối tượng nhận thông điệp. Áp
dụng phương thức này cho biến result sẽ trả về một chuỗi dạng ASCII của tập
này.
Thêm đoạn mã sau vào sau phần đoạn mã trên trong cửa sổ Workspace:
Transcript cr; show: ‘Your priorities are: ‘ ; cr;
nextPutAll: result printString.
Đoạn mã thêm vào này hiển thị thông tin của biến result không được tự
nhiên lắm. Chúng ta có thể lợi dụng một điểm là: lớp Collection, cha của lớp
SortedCollection, hiểu được toán tử lặp do:. Do đó, chúng ta có thể làm cho
Smalltalk hiển thị mỗi phần tử trong danh sách đã được sắp xếp trên từng dòng
riêng biệt bằng cách thay đổi dòng cuối cùng của đoạn như sau:
Transcript cr; show: ‘Your priorities are:’; cr.
result do: [ :element | Transcript show: element; cr].
4.5. Hoàn chỉnh phương thức Prioritize
Ở những phần trước, chúng ta đã xây dựng và sửa những lỗi(nếu có)
của phương thức mới này. Bây giờ chúng ta sẽ đặt tên cho nó và tìm nơi để
chứa nó. Chúng ta sẽ đặt tên cho phương thức này là prioritize. Để tiện việc
theo dõi và cũng để kiểm tra lại chương trình một lần nữa(double-check),
chúng ta sẽ liệt kê một cách hoàn chỉnh phương thức prioritize như sau:
prioritize
|anotherItem items result|
item := Set new.
[ (anotherItem := Prompter prompt:
‘Something to prioritize? (or leave blank)’
default: ‘’) = ‘’) whileFalse: [items add: anotherItem].
result := SortedCollection sortBlock:
[ :a :b | Prompter prompt: ‘Does ’, a printString,
‘have a higher priority than ’,
b printString, ‘?’
defaultExpression: ‘true’].
result addAll: items.
Transcript cr; show: ‘Your priorities are:’; cr.
result do: [ :element | Transcript show: element; cr].
Việc còn lại bây giờ của chúng ta là tìm nơi nào để lưu đoạn chương
trình này(thêm phương thức mới này vào cho lớp nào). Bởi vì ngay từ giai
đoạn thiết kế ban đầu, chúng ta đã có ý định là sẽ đưa ứng dụng này vào trong
2 menu là: System menu và Demo menu nên chúng ta sẽ chọn lớp thích hợp
để thêm phương thức này vào sao cho đáp ứng được yêu cầu thiết kế đã nêu.
4.6. Thêm phương thức Prioritizer vào hệ thống menu
Việc thêm khả năng gọi ứng dụng từ menu System và menu Demo
không đòi hỏi phải sử dụng(sub-classing) một lớp đã tồn tại của Smalltalk/V
nhưng thay vào đó, chúng ta phải sửa đổi đoạn mã Smalltalk/V đã có mới làm
được việc này.( Chú ý rằng việc sửa đổi trực tiếp mã nguồn trong Smalltalk
không phải là một cách lập trình tốt bởi vì mọi sự thay đổi của chúng ta sẽ có
ảnh hưởng lớn đến toàn hệ thống. Tuy nhiên, lý do chính đáng để chúng ta có
thể làm được điều này là do hệ thống menu của Smalltalk được hiện thực theo
cách cho phép người lập trình có thể thay đổi hành vi của chúng.)
Đến bước này, chúng ta nên lưu lại image đã làm được và đặt tên cho
Workspace lại là Prioritizer hay bất kỳ tên nào khác cũng được để có liên kết
đến ứng dụng này khi cần. Sau đó, đóng cửa sổ này lại.
4.7. Thay đổi menu Demo
Đầu tiên, chúng ta sẽ gắn ứng dụng của mình vào menu demo của
Smalltalk/V. Qua việc làm này chúng ta sẽ biết cách để biểu diễn ứng dụng
của mình với người khác và biết được cách làm việc với các hệ thống menu đã
có. Sau đó, chúng ta sẽ gắn ứng dụng này vào System menu một cách dễ dàng.
Để tạo bất kỳ một menu nào, chúng ta đơn giản tạo ra một thực thể mới
của lớp Menu với ít nhất hai tham số: một danh sách các nhãn để hiển thị và
một danh sách các chọn lựa hay phương thức để thực thi. Các dánh sách này
tương ứng một_một với nhau. Chúng ta còn có thể chia danh sách các nhãn
thành các nhóm nhỏ cách nhau bởi một đường thẳng bằng cách thêm vào tham
số lines.
Mở cửa sổ duyệt cây phân cấp lớp và chọn lớp DemoClass. Chú ý
rằng nó có một instance method gọi là demoMenu. Chúng ta sẽ thay đổi menu
này để thêm vào ứng dụng Prioritizer của chúng ta và chạy nó khi người sử
dụng chọn nó trong menu. Việc thêm này cũng khá đơn giản, nhưng trước hết,
chúng ta hãy xem phương thức demoMenu đã có gì trước khi thay đổi nó:
demoMenu
“Answer the menu for the receiver.”
^Menu
labels: (‘exit\dragon\mandala\multi mandala\’,
‘multi pentagon\multi spiral\bouncing ball’) withCrs.
lines: #(1 4)
selector: #(exit dragon mandala multiMandala
multiPentagon multiSpiral bounceBall)
Có một điều quan trọng cần chú ý ở đây là có một sự tương ứng trực
tiếp giữa thứ tự của các nhãn trong menu với thứ tự của phần chọn
lựa(selectors). Nhãn đầu tiên, exit, tương ứng với phần tử đầu tiên có tên
tương tự trong danh sách selectors(điều này không bắt buột, hai tên này có thể
khác nhau).
Để thêm phương thức mới của chúng ta vào lớp này, chúng ta sẽ thay
đổi nội dung của thông số labels: bằng cách thêm nhãn của phương thức này ở
cuối các keywords của tham số này. Sau đó, chúng ta sẽ thêm tên của phương
thức vào selectors tại vị trí tương ứng. Chúng ta cũng sẽ thêm một đường
thẳng vào trong menu để phân cách giữa mục chọn bouncing ball và mục chọn
prioritize mà chúng ta mới thêm vào bằng cách thêm số 9 vào trong dãy của
phần tham số lines:. Đoạn mã ở trên của chúng ta sẽ được thay đổi lại như sau:
demoMenu
“Answer the menu for the receiver.”
^Menu
labels: (‘exit\dragon\mandala\multi mandala\’,
‘multi pentagon\multi spiral\bouncing ball\prioritize’) withCrs.
lines: #(1 4 9)
selector: #(exit dragon mandala multiMandala
multiPentagon multiSpiral bounceBall prioritize)
Lưu đoạn mã này lại và chạy menu demo bằng cách chọn run Demo từ
menu hệ thống. Lúc này, chúng ta sẽ thấy rằng ứng dụng Prioritizer xuất hiện
như là một phần tử trong menu demo ở cuối cùng, ngăn cách với mục chọn
bouncing ball bởi một đường thẳng.(như hình…). Comment: Chèn hình vào đây
Để có thể truy xuất được ứng dụng Prioritizer mà chúng ta đã xây dựng
ở trên từ lớp DemoClass, chúng ta cần phải lưu đoạn mã của nó vào trong lớp
này(thêm một phương thức mới). Để làm được việc này, chúng ta tiến hành
các bước sau:
1. Chọn Workspace mà trước đây chúng ta đã lưu trữ tạm đoạn
mã Prioritizer
2. Chọn và Copy toàn bộ đoạn mã này
3. Mở cửa sổ Class Hierarchy Browser (CHB)
4. Chọn mục new method từ menu
5. Chọn tất cả đoạn text trong khung edit rồi chọn paste để dán
đoạn mã chúng ta vừa copy vào(chúng ta không nên nhấn phím
Backspace trong trường hợp này bởi vì nếu làm như vậy thì
chúng ta sẽ đưa phần vừa chọn vào thay thế đoạn mã đã được
Copy trước đó trong Clipboard)
6. Chọn save trong pane menu
Kể từ đây, ứng dụng Prioritizer của chúng ta đã có thể được thực thi
trực tiếp từ một mục chọn trong menu Demo.
4.8. Thay đổi menu hệ thống
Trước khi chúng ta tiến hành thay đổi trong hệ thống menu để truy
xuất đến ứng dụng Prioritizer, chúng ta phải hiểu được menu hệ thống hoạt
động như thế nào. Trước hết, mở menu hệ thống và nhấn phím Ctrl-Break để
mở cửa sổ Debugger. Điều này sẽ cho chúng ta một số khái niệm cơ bản về
menu hệ thống nhưng đây không phải là một cách tiếp cận tốt(Lý do sẽ được
trình bày sau).
Một cách khác để định vị một method là chúng ta lần theo vết về
những gì mà phương thức đó gọi. Chúng ta đã biết rằng menu Demo được
kích hoạt từ menu hệ thống, do đó, mở CHB, chọn lớp DemoClass, chọn
phương thức demoMenu và chọn senders trong pane menu. Chúng ta sẽ nhận
thấy rằng chỉ có phương thức run (cũng nằm trong DemoClass này) là có thể
gửi thông điệp demoMenu. Chọn phuơng thức run và chọn senders một lần
nữa, chúng ta sẽ thấy kết quả như trong hình…
Từ trên hình cho thấy chúng ta chỉ cần quan tâm đến một trong số các
senders mà thôi. Lớp ScreenDispatcher là lớp duy nhất gửi phương thức
runDemo do đó bây giờ chúng ta có thể chắc chắn rằng đây là nơi lưu trữ đoạn
mã của menu hệ thống. Mở CHB, chọn lớp này và duyệt qua các phương thức
của nó, chúng ta sẽ không thấy có gì có vẻ liên quan đến yêu cầu của chúng ta.
Click vào nút “class” bên dưới khung chứa danh sách các methods chúng ta sẽ
nhìn thấy các phương thức lớp của lớp này(hai phương thức trong trường hợp
này). Phương thức đầu tiên có vẻ hứa hẹn có tên là systemMenu. Tuy nhiên
nếu chọn phương thức này thì chúng ta sẽ thấy nó không có liên quan mấy đến
yêu cầu đặt ra. Nó chỉ có một dòng đề cập đến ScreenMenu nào đó. Do đó,
nhìn vào phương thức còn lại: initialize.(chúng ta chọn phương thức này vì dù
sao đi nữa menu hệ thống có lẽ được hình thành khi startup Smalltalk/V). Đến
đây, khi chọn phương thức lớp này và nhìn vào phần đoạn mã của nó thì
chúng ta thấy rằng nó hứa hẹn là sẽ đáp ứng được yêu cầu đặt ra. Sau đây là
toàn bộ đoạn mã của phương thức này:
initialize
“Private - Initialize the system menu.”
| aString |
BackupWindows
ifTrue: [
aString := ‘dos shell\speed/space\exit
Smalltalk\browse disk\open workspace\browse classes\redraw
screen\save image\run demo’ withCrs]
ifFalse: [
aString := ‘dos shell\speed/space\exit
Smalltalk\browse disk\open workspace\browse classes\redraw
screen\save image\run demo’ withCrs].
ScreenMenu := Menu
labels: aString
lines: #(3 7 9)
selectors: #(dosMenu speedSpace exit
openDiskBrowser openWorkspace openClassBrowser redraw save
runDemo)
Bây giờ chúng ta thấy rằng phương thức systemMenu chỉ làm nhiệm
vụ đơn giản là trả về ScreenMenu được tạo ra bởi phương thức initialize.
Từ những kinh nghiệm chúng ta đã làm trên menu Demo, chúng ta có
thể biết được những gì cần phải làm tiếp trong giai đoạn kế tiếp này. Chỉ cần
thêm nhãn prioritize vào cuối chuỗi aString và sau runDemo trong array
selectors:. Lưu lại chương trình.
Bây giờ, chúng ta sẽ tiến hành sao chép phương thức prioritize từ cửa
sổ Workspace vào lớp ScreenDispatcher theo các bước tương tự như khi thêm
vào cho lớp DemoClass ở phần trên và lưu phương thức này lại.
Tuy nhiên, trước khi có thể thực thi phương thức mới từ lớp
ScreenDispatcher chúng ta phải thực thi dòng lệnh sau từ cửa sổ Transcript
hay cửa sổ Workspace:
ScreenDispatcher initialize.
Chúng ta cần phải thực thi dòng lệnh này để lớp ScreenDispatcher tự
khởi tạo lại vì chúng ta cần thêm vào nó một phương thức mới mà điều này chỉ
được làm khi khởi tạo nó và thường thì menu này sẽ không thay đổi trong suốt
quá trình lập trình.
4.9. Hoàn thiện ứng dụng
Trong phần này chúng ta sẽ thực hiện 2 việc để hoàn thành ứng dụng
này: thay đổi cách trả lời true_false “khó chịu” của prompter và loại bỏ sự cần
thiết phải có 2 bản sao của đoạn mã như đã hiện thực bằng cách tạo ra một lớp
mới cho ứng dụng Prioritizer và xóa đoạn mã cũ không còn cần thiết.
4.10. Thay đổi Prompter
Chúng ta đã sử dụng một thực thể của lớp Prompter để tạo tất cả các
thành phần giao tiếp với người sử dụng trong ứng dụng của chúng ta. Vấn để ở
đây là một thực thể prompter chỉ làm được một việc mà thôi đó là: đưa yêu
cầu cho người và nhận đáp ứng từ người sử dụng(hay là sử dụng đáp ứng mặc
định) trong khi bây giờ điều chúng ta muốn ở đây lại là cho phép người sử
dụng click vào từ “yes” hay “no” để cung cấp các đáp ứng của họ.
Tuy các prompter chỉ cho phép người sử dụng gõ vào các đáp ứng
nhưng chúng ta cũng có một thành phần giao diện khác cho phép người sử
dụng đáp ứng bằng cách click vào các lựa chọn và đối tượng này dĩ nhiên là
pop-up menu, thuộc vào lớp Menu. Do vậy, đây sẽ là nơi chúng ta sẽ làm việc
để đạt được yêu cầu về việc thay đổi cách đáp ứng của người sử dụng.
Chúng ta sẽ làm tương tự với các vấn đề về việc yêu cầu người sử
dụng sắp xếp các danh sách các lựa chọn.
Chúng ta sẽ tạo một phương thức mới có tên là confirm: cho lớp Menu
để có một menu pop up có hai lựa chọn: yes và no. Phương thức này có đối số
là câu hỏi mà chúng ta sẽ đưa cho người sử dụng. Chú ý là chúng ta không nên
mã cố định câu hỏi này vì chúng ta muốn tổng quát hóa phương thức này.(Đây
cũng chính là một mục tiêu quan trọng trong việc thiết kế hướng đối tượng).
Mở CHB, chọn lớp Menu, nhấn nút Class rồi chọn new method từ pane
menu và gõ đoạn mã sau vào:
confirm: queryString
“Return Boolean true for Yes, false for No.”
| labelTmp tempMenu respone |
labelTmp := menuString, ‘\Yes\No’.
tempMenu := Menu
labels: labelTmp withCrs
lines: #(1)
selectors: (Array with: true with: true with: false).
^tempMenu popUpAt: Cursor offset.
Ở dòng 4 của phương thức này, chúng ta nối câu hỏi được truyền thông
qua tham số khi phương thức được gọi với các nhãn “Yes” và “No” để tạo nên
nội dung của menu pop-up. Do người sử dụng cũng có thể chọn vào câu hỏi
(điều này ít khi xảy ra) nên chúng ta cũng phải cung cấp giá trị cho nó ở trong
phần selector. Các dấu backslashes(\) trong phần tham số labels: dùng để ngăn
cách giữa các thành phần trong menu và được chuyển thành dấu xuống
dòng(carriage return) sau khi chúng ta gửi đến chuỗi này thông điệp withCrs.
Thông số labels: cung cấp một chuỗi mà trong đó tất cả các dấu \ đều
được chuyển thành dấu xuống dòng và ở dòng kế tiếp chúng ta sẽ đặt một
đường thẳng ngăn cách sau dòng 1 để ngăn cách giữa chọn lựa Yes và No.
Tham số selectors: trả về giá trị true tương ứng khi người sử dụng click vào
câu hỏi hay câu trả lời đầu tiên(Yes) và false nếu người sử dụng chọn No. Chú
ý rằng chúng ta phải đặt kiểu tường minh của tham số selectors: là kiểu array
vì nếu không thì phương thức này sẽ không trả về một giá trị kiểu Boolean mà
trả về các ký hiệu #true và #false. Khi đó, hiển nhiên sortBlock sẽ không thể
hiểu được các ký hiệu này và gây ra lỗi.
Dòng cuối cùng của đoạn mã trên dùng để báo cho thực thể tempMenu
của lớp Menu biết vị trí đặt menu pop up là tại vị trí hiện tại của con trỏ. Dấu ^
dùng để trả về giá trị của phương thức.
Bây giờ chúng ta sẽ thay đổi phương thức prioritize một ít để có thể sử
dụng được phần giao diện người dùng mới này. Chọn phương thức prioritize
trong lớp DemoClass từ CHB và thay đổi dòng mã dùng để gán danh sách đã
sắp xếp vào biết result như sau:
result := SortedCollection sortBlock:
[ :a :b| Menu confirm: ‘Does ’, a printString,
‘ have a higher priority than ‘,
b printString, ‘ ?’
].
Tất cả các dòng mã khác trong method này đều không có gì thay đổi.
Save phương thức này lại và làm tương tự với phương thức prioritize trong lớp
ScreenDispatcher. Bây giờ chúng ta có thể kiểm tra thử các phương thức này.
Chúng ta sẽ thấy có một pop up menu hiển thị lên đê yêu cầu người sử dụng
sắp xếp danh sách các lựa chọn. Người sử dụng lúc này chỉ cần click chuột
vào câu trả lời mà họ muốn. Nếu người sử dụng, vì một lý do nào đó click
chuột vào câu hỏi thì chúng ta cũng nhận được câu trả lời đã được đặt mặc
định là “Yes” (chúng ta hoàn toàn có thể thay đổi giá trị này thành “No” hay
thậm chí có thể có một phương thức để giải quyết vấn đề này bằng cách thay
đổi giá trị của phần tử đầu tiên trong dãy của tham số selectors:)
4.11. Hợp nhất mã(Consolidating the code)
Trước đây, trong quá trình xây dựng ứng dụng Prioritize để cho đơn
giản, chúng ta đã dùng 2 phiên bản giống nhau để tạo hai phương thức trong
hai lớp khác nhau. Nhưng nếu vẫn để như vậy thì ứng dụng của chúng ta sẽ
thiếu tính nhất quán. Do đó, chúng ta sẽ tạo ra một lớp mới chứa phương thức
này để giải quyết vấn đề dư thừa mã này và sau đó gọi nó từ lớp DemoClass
và ScreenDispatcher.
Đầu tiên chép phần đoạn mã của phương thức prioritizer vào trong
clipboard bằng cách mở cửa sổ CHB, chọn phương thức này trong lớp
DemoClass hoặc lớp ScreenDispatcher. Sau đó, chọn lớp tổng quát nhất (lớp
Object) rồi chọn add subclass từ pane menu.
Chúng ta sẽ thấy hệ thống yêu cầu cung cấp tên cho lớp con của lớp
Object mới này. Chúng ta nhập vào tên lớp này là “Prioritizer”và nhấn enter.
Sau đó, chúng ta sẽ nhìn thấy một menu pop up có vẻ khó hiểu yêu cầu chúng
ta chọn kiểu lớp con mà chúng ta sẽ tạo. Đừng quan tâm đến ý nghĩa của
chúng lúc này, cứ chọn “subclass” option. Tiếp theo, không cần thay đổi gì
trong đoạn mô tả lớp và do CHB đã tự động chọn lớp mới này nên chúng ta cứ
chọn new method từ ô danh sách các method còn trống ở góc phải trên. Một
mẫu (template) của một method mới sẽ xuất hiện trong khung soạn thảo
(editing pane). Chọn tất cả đoạn text này rồi chọn paste trong pane menu.(chú
ý không nhấn vào phím backspace trong lúc này nếu không dữ liệu trong
clipboard sẽ bị xóa).
Để thống nhất với các qui tắc của Smalltalk/V chúng ta sẽ đổi tên
method của chúng ta thành run để nghe có vẻ như nó sẽ thực sự thực hiện
những gì mà chúng ta yêu cầu từ các lớp gọi nó.
Chọn tên phương thức prioritize và đổi lại thành run.
Chọn save từ pane menu.
Bây giờ, chúng ta có thể sửa 2 phương thức prioritize trong lớp
DemoClass và ScreenDispatcher để gọi phương thức vừa được tạo ở trên. Hai
phương thức này sẽ được thay đổi một cách tương tự nhau.
Chọn phương thức prioritize của một trong hai lớp trên từ CHB rồi xóa
toàn bộ phần đoạn mã của nó(có thể xóa luôn các comments). Sau đó, gõ dòng
mã sau vào:
Prioritizer new run.
Dòng mã này dùng để tạo ra một thực thể mới của lớp Prioritizer và
thực thi phương thức run mà chúng ta vừa định nghĩa ở trên.
(Tất cả các lớp đều biết cách tự tạo ra các thực thể mới nên chúng ta
không cần phải định nghĩa phương thức new trừ khi chúng ta muốn làm gì
khác ngoài những gì được thừa kế từ hành vi new)
Do chúng ta không thay đổi gì đối với các menu trong lớp DemoClass
và lớp ScreenDispatcher nên chúng ta vẫn có thể gọi ứng dụng Prioritizer từ
chúng theo cách tương tự như trước đây.
4.12.
Tài liệu tham khảo:
1/ Smalltalk/V Tutorial
2/ Dan Shafer and Dean A.Ritz Practical Smalltalk Using smalltalk/V
3/ Henri E.Bal Dick Grune Programming language Essentials
PHẦN III: LISP
Tiết 11: Giới thiệu ngôn ngữ LISP
Tiết 12: Thao tác trên ký hiệu(Symbol Manipulation), các thành phần cơ bản của
LISP
Sinh viên cần nắm được các nội dung sau:
• Hiểu được các thao tác trên ký hiệu của Lisp (Symbol Manipulation)
• Tại sao lại chọn Lisp để thao tác trên các ký hiệu
• Các kiểu dữ liệu cơ bản và các symbol_manipulation primitive trong Lisp
• Môi trường làm việc của GCLISP
• Cách soạn thảo và load chương trình
Bài tập / bài thực hành:
1/Tính các biểu thức sau trong CLISP
a/(+ 3.14 2.71)
b/(+ (* 2 2) (/ 2 2))
c/(/ (+ 3 1) (- 3 1))
d/(* (MAX 3 4 5) (MIN 3 4 5))
e/(MIN (MAX 3 1 4) (MAX 2 7 1))
2/Trong các đối tượng sau đối tượng nào thuộc loại atom, list hoặc không thuộc loại
nào trong hai loại này
ATOM
(THIS IS AN ATOM)
(THIS IS AN EXPRESSION)
((A B) (C D)) 3 (3)
(LIST 3)
(/ (+ 3 1) (- 3 1))
)(
((()))
(()())
((())
())(
((ABC
3/Xác định giá trị của các dạng(form) sau:
(first `(p h w))
(rest `(b k v n))
(first `((a b) (c d)))
(first (rest ‘((a b) (c d))))
(rest (first ‘((a b) (c d))))
(rest (first (rest ‘((a b) (c d)))))
(first (rest (first ‘((a b) (c d)))))
4/Xác định giá trị của các form sau:
(first (rest (first (rest ‘((a b) (c d) (e f))))))
(first (first (rest (rest ‘((a b) (c d) (e f))))))
(first (first (rest ‘(rest ((a b) (c d) (e f))))))
(first (first ‘(rest (rest ((a b) (c d) (e f))))))
(first ‘(first (rest (rest ((a b) (c d) (e f))))))
‘(first (first (rest (rest ((a b) (c d) (e f))))))
5/Chỉ sử dụng các primitives FIRST và REST để chọn ra symbol PEAR từ các biểu
thức sau:
(apple orange pear grapefruit)
((apple orange) (pear grapefruit))
(((apple) (orange) (pear) (grapefruit)))
(apple (orange) ((pear)) (((grapefruit))))
((((apple))) ((orange)) (pear) grapefruit)
((((apple) orange) pear) grapefruit)
6/Xác định giá trị của các dạng sau
(append ‘(a b c) ‘( ))
(list ‘(a b c) ‘( ))
(cons ‘(a b c) ‘( ))
7/Xác định giá trị của các dạng(form) sau theo đúng thứ tự được cho
(setf tools (list ‘hammer ‘screwdriver))
(cons ‘pliers tools)
tools
(setf tools (cons ‘pliers tools))
tools
(append ‘(saw wrench) tools)