Lưu ý: Nếu trong các tham số của một vị từ có biến thì biến này phải xuất hiện ít nhất 2 lần trong vị từ đó hoặc trong các vị từ dùng để suy diễn ra vị từ đó.. goal: Ở phần này, ngườ
Trang 1Biểu diễn tri thức và suy luận
LỜI NÓI ĐẦU
Prolog - xuất phát từ cụm từ tiếng Pháp Programmation en logique - là "lập trình theo lô gích" Prolog xuất hiện từ năm 1972, do Alain Colmerauer và Robert Kowalski thiết kế Mục tiêu của Prolog là giúp người dùng mô tả lại bài toán bằng ngôn ngữ của logic Dựa trên đó, máy tính sẽ tiến hành suy diễn tự động dựa vào những cơ chế suy diễn có sẵn (hợp nhất, quay lui và tìm kiếm theo chiều sâu) để tìm câu trả lời cho người dùng
Prolog được sử dụng nhiều trong các ứng dụng của trí tuệ nhân tạo và ngôn ngữ học trong khoa học máy tính, đặc biệt là trong ngành xử lý ngôn ngữ tự nhiên Cú pháp và ngữ nghĩa của Prolog đơn giản
và sáng sủa Nó được người Nhật coi là một trong những nền tảng để xây dựng máy tính thế hệ thứ năm, mà ở đó thay vì phải mô tả cách giải quyết một bài toán trên máy tính, con người chỉ cần mô tả bài toán và máy tính sẽ hỗ trợ họ nốt phần còn lại
Trong bài thu hoạch môn học Biểu diễn tri thức và suy luận này, em xin trình bày khái quát về
ngôn ngữ Prolog và ứng dụng lập trình Prolog cho 3 bài toán: tháp Hà Nội, tính giai thừa, và lập gia
hệ
Trang 2Biểu diễn tri thức và suy luận
I Ngôn ngữ Prolog
1 Giới thiệu
Prolog là một ngôn ngữ lập trình Tên gọi Prolog được xuất phát từ cụm từ tiếng Pháp Programmation en logique, nghĩa là "lập trình theo lô-gích" Xuất hiện từ năm 1972 (do Alain Colmerauer và Robert Kowalski thiết kế)
Mục tiêu của Prolog là giúp người dùng mô tả lại bài toán trên ngôn ngữ của logic Dựa trên đó, máy tính sẽ tiến hành suy diễn tự động dựa vào những cơ chế suy diễn có sẵn (hợp nhất, quay lui
và tìm kiếm theo chiều sâu) để tìm câu trả lời cho người dùng
Prolog được sử dụng nhiều trong các ứng dụng của trí tuệ nhân tạo và ngôn ngữ học trong khoa học máy tính (đặc biệt là trong ngành xử lý ngôn ngữ tự nhiên vì đây là mục tiêu thiết kế ban đầu của nó)
Cú pháp và ngữ nghĩa của Prolog đơn giản và sáng sủa Nó được người Nhật coi là một trong những nền tảng để xây dựng máy tính thế hệ thứ năm: thay vì phải mô tả cách giải quyết một bài toán trên máy tính, con người chỉ cần mô tả bài toán và máy tính sẽ hỗ trợ họ phần còn lại
Nguyên lý của lập trình theo lô-gích dựa trên các mệnh đề Horn Một mệnh đề Horn biểu diễn một sự kiện nào đó là đúng hoặc không đúng, xảy ra hay không xảy ra
Ví dụ:
Thạch là một học viên cao học
Nếu Thạch học tốt thì sẽ được làm luận văn
Nếu A là cha mẹ của B và B là cha mẹ của C thì A là ông bà của C
2 Các thuật ngữ:
Một chương trình Prolog là một cơ sở dữ liệu gồm các mệnh đề (clause) Mỗi mệnh đề được xây dựng từ các vị từ Một vị từ có thể có một hoặc nhiều nguyên tử logic (logic atom) Mỗi logic atom biểu diễn quan hệ giữa các hạng (term) Term có thể sơ cấp (elementary term) như các hằng, các biến và phức hợp (compound term)
Các term phức hợp biểu diễn các đối tượng phức tạp của bài toán đang xét Term phức hợp là một hàm tử (functor) có đối số (argument), các functor này có dạng:
Tên_functor(Đối_1, Đối_2,…, Đối_n)
Trong đó:
Tên_functor là một chuỗi gồm các chữ cái và chữ số và được bắt đầu bằng một chữ cái thường
Các đối có thể là biến, term sơ cấp hoặc phức hợp
Mệnh đề có thể là một sự kiện, một luật hay một câu hỏi Prolog quy ước sau mỗi mệnh đề cần có một dấu chấm để kết thúc
Trang 3Biểu diễn tri thức và suy luận
3 Các kiểu dữ liệu:
Prolog có hai dạng kiểu dữ liệu: sơ cấp và có cấu trúc
Kiểu dữ liệu sơ cấp: gồm một số kiểu dữ liệu được định nghĩa sẵn trong Prolog như:
char: kiểu ký tự, khi sử dụng đặt trong cặp dấu nháy đơn.Ví dụ: ‘a’, ‘b’, …
string: kiểu chuỗi, khi sử dụng đặt trong cặp dấu nháy đôi Ví dụ: “hoc vien cao hoc”
integer: kiểu số nguyên
unsigned: kiểu số nguyên dương không dấu
real: kiểu số thực
Kiểu dữ liệu có cấu trúc: do người dùng tự định nghĩa dựa trên các kiểu sơ cấp đã có sẵn
Có một số trình biên dịch ngôn ngữ Prolog không yêu cầu người dùng phải khai báo kiểu dữ liệu được dùng (ví dụ như SWI-Prolog) Tuy nhiên, việc khai báo này là cần thiết đối với các hàm hay
vị từ trong Visual Prolog
4 Chú thích
Tương tự như các ngôn ngữ khác, trong một chương trình Prolog, người dùng có thể thêm vào các chú thích (comment) Khi muốn chú thích cho một dòng, có thể sử dụng dấu % và sử dụng cặp dấu /* và */ khi chú thích nhiều dòng
Tên các biến sau là không hợp lệ: uoc_so, 2n, 345, …
Tên các biến sau là hợp lệ: X, Ten, _, _Phai, Uoc_so, …
Trong Prolog, có một biến đặc biệt không cần chỉ định kiểu khi sử dụng, đó là dấu gạch dưới _ Dấu gạch dưới được xem như một biến, sử dụng cho một vị từ nào đó có một đối mà với bất kỳ giá trị nào của đối này cũng nhận được một giá trị duy nhất
6 Phép toán số học:
Prolog chủ yếu hỗ trợ xử lý các ký hiệu Tuy nhiên, nó cũng hỗ trợ một số phép toán hai ngôi chuẩn như sau:
Trang 4Biểu diễn tri thức và suy luận
“2 là một số nguyên tố” là một sự kiện vì nó diễn tả sự thật
Ta có thể diễn đạt dưới một vị từ trong Prolog như sau: nguyen_to(2)
Ví dụ: Xét cây phả hệ như sau:
Trang 5Biểu diễn tri thức và suy luận
Sự kiện “an là cha mẹ của hạnh” sẽ được diễn đạt bằng một vị từ như sau:
cha_me(“an”, “hanh”)
Vị từ cha_me có 2 đối là “an” và “hanh”
Dưới đây là 6 vị từ diễn đạt cho cây phả hệ trên:
Luật: là một vị từ diễn tả quy luật suy diễn được công nhận đúng Luật được trình bày dưới dạng
một mệnh đề Trong Prolog, để định nghĩa cho một luật, ta sử dụng cặp ký tự :-
Xét định nghĩa sau: “N là một số nguyên tố nếu N>0, M là số nguyên tố nào đó, M<N và
N không chia hết cho M”
Khi biểu diễn bằng Prolog, ta có thể viết như sau: nguyen_to(N):-N>0, nguyen_to(M), M<N, N mod M <> 0
Phân biệt sự kiện và luật:
Sự kiện là một điều luôn đúng và không có ràng buộc
Luật thì phải phụ thuộc vào các biến hay thuộc tính và khi thoả mãn điều kiện nào đó thì
là đúng
Một luật thường gồm hai phần:
o Phần bên trái dấu :- được gọi là kết luận hay phần đầu (head) của luật
o Phần bên phải dấu :- được gọi là điều kiện hay phần thân (body) của luật
Nếu điều kiện đúng thì phần kết luận cũng đúng Đó là hậu quả logic của phép suy luận (inference)
8 Chương trình Prolog:
Trang 6Biểu diễn tri thức và suy luận
Một chương trình Prolog thường gồm 4 phần cơ bản như sau: domains, predicates, clauses và goal
domains:
Đây là phần định nghĩa kiểu dữ liệu mới dựa vào các kiểu dữ liệu đã biết
Các kiểu dữ liệu được định nghĩa ở đây sẽ được sử dụng cho các đối số trong các vị từ
Nếu các vị từ sử dụng đối số có kiểu cơ bản thì có thể không cần phải định nghĩa lại các kiểu đó Tuy nhiên, ta nên định nghĩa lại các kiểu cơ bản để chương trình sáng sủa
Cú pháp:
<danh sách kiểu mới> = <kiểu đã biết>
hoặc
<danh sách kiểu mới> = <danh sách kiểu đã biết>
Trong đó, <kiểu đã biết> có thể là kiểu cơ bản (như số nguyên, số thực, …) hoặc các kiểu
đã được định nghĩa trước Các kiểu mới phân cách nhau bởi dấu phẩy, còn các kiểu đã biết phân cách nhau bởi dấu chấm phẩy ;
Ví dụ:
domains
ten, tac_gia, nha_xb, dia_chi = string
nam, thang, so_luong = integer
dien_tich = real
nam_xb = nxb(thang, nam)
do_vat = sach(tac_gia, ten, nha_xb, nam_xb); xe(ten, so_luong); nha(dia_chi, dien_tich) Trong ví dụ trên, ta đã định nghĩa các kiểu mới ten, tac_gia, nha_xb, dia_chi dựa vào cùng một kiểu cơ bản đã biết là string
Đồng thời, ta định nghĩa các kiểu mới nam, thang, so_luong dựa vào cùng một kiểu đã biết là integer
Kiểu mới dien_tich dựa vào kiểu đã biết là real
Kiểu mới nam_xb dựa vào kiểu nxb được xây dựng từ các kiểu đã biết là thang, nam
Kiểu do_vat dựa vào các kiểu sach, xe, nha mà các kiểu này lại dựa vào các kiểu đã biết
Trang 7Biểu diễn tri thức và suy luận
Trong ví dụ trên ta khai báo 4 vị từ Trong đó:
Vị từ so_huu (ten, do_vat) để chỉ một người có tên là ten sẽ sở hữu môt do_vat nào đó
Vị từ nguyen_to(integer) để xét xem một số nguyên nào đó có phải là số nguyên tố hay không
Vị từ nguoi(ten) để xét xem một cái tên ten nào đó có là người không
Vị từ cha_me(ten, ten) xét xem một người có tên là ten (đối thứ nhất) có là cha mẹ của người có tên là ten (đối thứ hai) hay không
clauses:
Đây là phần bắt buộc phải có, dùng để mô tả các sự kiện và các luật, sử dụng các vị từ đã khai báo trong phần predicates
Cú pháp:
<Tên vị từ>(<danh sách các tham số>) <kí hiệu>
<Tên vị từ 1>(<danh sách các tham số 1>) <kí hiệu>
…
<Tên vị từ N>(<danh sách các tham số N>) <kí hiệu>
Trong đó, Tên vị từ phải được khai báo trong phần predicates
Các tham số có thể là các hằng hoặc biến có kiểu tương thích với các kiểu tương ứng đã được khai báo trong các vị từ ở trong phần predicates và được viết cách nhau bởi dấu phẩy
nguyen_to(N):-N>0, nguyen_to(M), M<N, N mod M <>0
so_huu(“Tran Thi HongYen”, sach(“Do Van Nhon”, “Giao Trinh Toan Roi Rac”, “Khoa Hoc Co Ban”, nxb(9,2006)))
nguoi(“Socrates”)
Trang 8Biểu diễn tri thức và suy luận
chet(X):-nguoi(X)
Lưu ý: Nếu trong các tham số của một vị từ có biến thì biến này phải xuất hiện ít nhất 2 lần trong
vị từ đó hoặc trong các vị từ dùng để suy diễn ra vị từ đó Nếu chỉ xuất hiện một lần thì bắt buộc phải dùng biến tự do
Ví dụ:
Để diễn tả sự kiện: Tổ hợp chập 0 của N bất kỳ bằng 1, ta không thể viết To_hop(N,0,1)
vì biến N chỉ xuất hiện đúng một lần trong vị từ này, mà phải viết là To_hop(_,0,1)
goal:
Ở phần này, người dùng sẽ đưa ra các mục tiêu cần tìm kết quả, thực chất là các câu hỏi
mà người dùng muốn Prolog trả lời dựa vào các sự kiện và luật đã được khai báo và định nghĩa ở trên
Cú pháp phần goal giống như cú pháp phần clauses, nghĩa là đưa vào một hoặc một số các
vị từ Nếu tất cả các tham số của vị từ là hằng thì kết quả nhận được là true (hoặc Yes - đúng) hoặc false (hoặc No - sai) Nếu trong các tham số của vị từ có biến thì kết quả trả về
sẽ là các giá trị của biến
Ví dụ:
Muốn biết 4 có phải là số nguyên tố không thì trong phần goal sẽ nhập vị từ sau: nguyen_to(4), và kết quả nhận được sẽ là false
Nếu phần goal được nhập là nguyen_to(X), thì câu trả lời là X=2
Ngoài các phần chủ yếu nói trên, có thể đưa vào các phần liên quan đến khai báo hằng, các tập tin liên quan hoặc chỉ thị dịch
Ví dụ:
Để khai báo hằng số Pi, ta viết:
constants
Pi = 3.141592653
Lưu ý: tùy theo trình biên dịch, thể hiện phần goal sẽ khác nhau Một số trình biên dịch cho người
dùng nhập phần goal sau dấu nhắc ?- (như B-Prolog hay SWI-Prolog) Tuy nhiên, cũng có trình biên dịch như Turbo-Prolog hay Visual Prolog thì các mục tiêu viết sau từ khóa goal
9 Phép hợp nhất
Công việc quan trọng nhất của Prolog trong việc tìm câu trả lời là thực hiện việc hợp nhất Phép hợp nhất sẽ được biểu diễn bởi dấu = Nó có hai thành phần: vế trái và vế phải Phép hợp nhất sẽ trả về kết quả true (thành công) hoặc false (thất bại)
Có các trường hợp hợp nhất sau:
Trang 9Biểu diễn tri thức và suy luận
a Cả hai vế đều là hằng hoặc biểu thức chứa toàn hằng: nếu giá trị của hai vế là bằng
nhau thì phép hợp nhất thành công (kết quả là true), ngược lại phép hợp nhất sẽ thất bại (kết quả là false)
“xyz” = “xyz” true
“xyzt” = “xyz” false
b Một trong hai vế là hằng hoặc trong biểu thức chứa toàn hằng, vế kia là biến hoặc biểu thức có chứa biến
Nếu tất cả các biến đều có giá trị (các biến ở tình trạng bound) thì quay về trường hợp a
9 = X false nếu X đã có giá trị là 8
9 = X +1 true nếu X đã có giá trị là 8
Y = “Socrates” true nếu Y đã có giá trị là “Socrates”
Nếu có biến chưa có giá trị (biến ở tình trạng unbound), Prolog sẽ gán giá trị cho biến sao cho hai vế có giá trị như nhau và trả về kết quả là true Nếu không tìm giá trị như vậy, phép hợp nhất sẽ cho kết quả là false
9 = X true nếu X chưa có giá trị, sau phép hợp nhất này, X sẽ có giá trị là 9
-1 = X*X false vì không thể tìm cho X giá trị nào làm cho giá trị hai vế là như nhau
c Cả hai vế đều là biến hoặc các biểu thức có chứa biến:
Nếu tất cả các biến đều có chứa giá trị thì quay về trường hợp a
X = Y true nếu cả X và Y đều đã có giá trị và những giá trị này bằng nhau
X - 1 = Y false nếu X và Y đều đã có giá trị và X nhỏ hơn Y
Nếu tất cả các biến của một vế đều đã có giá trị, quay về về trường hợp c
X = Y true nếu X chưa có giá trị và Y đã có giá trị, sau phép hợp nhất, X sẽ nhận giá trị của Y
X - 1 = Y true nếu X chưa có giá trị, Y đã có giá trị Sau phép hợp nhất, X sẽ có giá trị bằng Y +1
Cả hai vế đều còn chứa biến ở tình trạng unbound thì hợp nhất vẫn thành công và mỗi khi một biến nào đó trong vế phải hoặc vế trái có giá trị thì biến còn lại cũng sẽ được ràng buộc với giá trị đó
X = Y true nếu cả X và Y đều chưa gán giá trị
X - 1 = Y true nếu cả X và Y đều chưa gán giá trị
10 Cơ chế tìm câu trả lời của Prolog:
Khi nhận được một câu hỏi ở phần goal thì Prolog sẽ thực hiện công việc so khớp (match), tức là tìm mệnh đề đầu tiên đề cập đến khái niệm mà nó nhận được Cụ thể là Prolog sẽ dùng phép hợp nhất đã trình bày ở trên trong quá trình so khớp cấu trúc dữ liệu một mục tiêu với một mệnh đề Giả sử người dùng đặt ra câu hỏi như sau: nguoi(“Socrates”)
Trang 10Biểu diễn tri thức và suy luận
Prolog sẽ tìm mệnh đề đầu tiên có liên quan đến khái niệm nguoi Và mệnh đề đầu tiên và duy nhất có liên quan đến khái niệm này là: nguoi(“Socrates”) Như vậy, khi đã có câu hỏi nguoi(“Socrates”) và tìm thấy mệnh đề liên quan nguoi(“Socrates”)., Prolog sẽ tiến hành tìm kiếm lời giải Công việc này tiến hành bằng cách tạo mối liên kết giữa các thông số ở phần câu hỏi và các thông số ở phần mệnh đề
Sau khi đã tạo mối quan hệ giữa các thông số ở phần câu hỏi và phần mệnh đề, Prolog sẽ tiến hành các mệnh đề (nếu mệnh đề này một luật) Nếu tất cả các mệnh đề thành công và các biến ở phần câu hỏi đã ở tình trạng bound (tức là đã có giá trị), Prolog sẽ thông báo lời giải
Nếu là câu hỏi thuộc dạng true/false (Yes/No) như ví dụ trên, tức là câu hỏi không chứa biến, Prolog sẽ trả lời true (Yes) nếu công việc hợp nhất thành công và các mệnh đề đều thành công (nếu mệnh đề so khớp là một luật)
Ở ví dụ trên, thông số của câu hỏi là một hằng “Socrates”, và thông số của mệnh đề tương ứng cũng là một hằng “Socrates” Hai hằng này hợp nhất thành công, và kết quả trả lời là true(Yes) Nếu người dùng đặt ra câu hỏi khác: nguoi(“Xeda”), Prolog cũng chỉ tìm thấy một mệnh đề liên quan đến khái niệm này là nguoi(“Socrates”) Vì sự hợp nhất giữa hai hằng “Socrates” và “Xeda” thất bại nên kết quả sẽ là false(No)
Giả sử câu hỏi có chứa biến: nguoi(X) Hệ thống sẽ tìm thấy mệnh đề có liên quan đến vấn đề này
là nguoi(“Socrates”), rồi tiến hành hợp nhất giữa X và “Socrates” Vì X chưa có giá trị (unbound) nên phép hợp nhất thành công Kết quả X có giá trị là “Socrates”
Khi việc hợp nhất các thông số giữa phần câu hỏi và phần mệnh đề đã thành công, tất cả các biến cần tìm đã có giá trị (ở đây chỉ có một biến là X), thì hệ thống sẽ thông báo đã tìm ra lời giải và in
ra giá trị của X ( X = “Socrates”)
Xét trường hợp câu hỏi so khớp với một luật: chet(Y) Câu hỏi được so khớp với mệnh đề sau: chet(X): - nguoi (X) Vì hai biến X (đối của mệnh đề) và Y (đối của câu hỏi) đều chưa chứa giá trị, hệ thống sẽ xem cả hai biến là một, nghĩa là khi X có được giá trị thì Y cũng có giá trị đó và ngược lại
Quá trình thực hiện các mệnh đề con ở vế phải sẽ được thực hiện như sau:
Nếu mệnh đề con này có thông số là biến unbound, Prolog sẽ tìm giá trị của biến này để mệnh đề con có giá trị true(Yes) Nếu không tìm được, mệnh đề con sẽ thất bại
Nếu mệnh đề con có đối đều là biến bound (đã có giá trị) hoặc là hằng, Prolog sẽ kiểm tra xem mệnh đề con có trả về giá trị true(Yes) hay không Nếu không, mệnh đề con sẽ thất bại
Các mệnh đề con sẽ được tiến hành từ trái qua phải, và nếu có một mệnh đề con thất bại, mệnh
đề được so khớp sẽ thất bại
Ở trường hợp trên, khi tiến hành mệnh đề nguoi(X), vì biến X là unbound (trường hợp 9.1) nên hệ thống sẽ tìm giá trị của X cho mệnh đề con trên là đúng Cách tìm kiếm câu trả lời cho mệnh đề
Trang 11Biểu diễn tri thức và suy luận
con này hoàn toàn giống như cách hệ thống tìm câu trả lời khi đặt câu hỏi này trong phần câu hỏi
sungsuong(X) :- nguoi(X), vua(X)
Trong ví dụ này, ngoài khái niệm về người, còn có khái niệm về vua và sự sung sướng Ta có thể diễn giải những thông tin trong các vị từ trên thành ngôn ngữ tự nhiên như sau: "Có hai người tên
là Socrates và Xeda Một người là vua tên Xeda” Và một luật là: “một thực thể nào đó chỉ sung sướng nếu thực thể đó vừa là người vừa là vua."
Giả sử người dùng nhập câu hỏi sau: sungsuong(X) thì Prolog sẽ hiển thị câu trả lời thế nào? Trước tiên, Prolog sẽ so khớp câu hỏi trên với mệnh đề sungsuong(X) :- nguoi(X),vua(X) Nhưng khi so khớp câu hỏi với mệnh đề, do cả hai biến X lúc này đều chưa chứa giá trị, nên chúng sẽ được xem như một
Sau đó, Prolog sẽ tiến hành các mệnh đề con Ở mệnh đề con đầu tiên, nguoi(X) tương tự như ví
dụ ở phần trên, Prolog sẽ tìm được câu trả lời là X = “Socrates” Khi thực hiện mệnh đề con thứ hai vua(X), lúc này X đã có giá trị là Socrates nên Prolog sẽ kiểm tra xem giá trị này có làm giá trị của mệnh đề là true hay không
Việc tiến hành trả lời một mệnh đề con cũng tương tự như khi trả lời một câu hỏi, Prolog lại so khớp mệnh đề con với một mệnh đề cùng tên Prolog tìm thấy một mệnh đề liên quan đến vua là vua(“Xeda”) và tiến hành hợp nhất giữa X và Xeda Do X đã có giá trị là Socrates, việc hợp nhất thất bại
Tuy nhiên, khi mệnh đề con này thất bại, Prolog chưa kết luận vội là mệnh đề này thất bại Hệ thống sẽ quay lại thời điểm biến X được gán giá trị, tức là khi trả lời mệnh đề con nguoi(X), X được chuyển lại sang tình trạng unbound và cố gắng tìm kiếm một giá trị khác của X để cho mệnh
đề con này vẫn đúng Công việc này được gọi là sự quay lui (back-tracing)
Do việc so khớp mệnh đề con này với mệnh đề nguoi(“Socrates”) thất bại, hệ thống sẽ tiếp tục so khớp với mệnh đề khác Nếu không còn mệnh đề nào khác liên quan đến mệnh đề con thì việc thực hiện mệnh đề mới thật sự thất bại Ở đây, hệ thống tìm thấy một mệnh đề khác liên quan