1. Trang chủ
  2. » Địa lý lớp 12

Giáo trình Lập trình logic trong prolog: Phần 2 - NXB Đại học Quốc gia

20 13 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 203,9 KB

Nội dung

Viết chương trình Prolog chuyển một danh sách phức hợp, là danh sách mà mỗi phần tử có thể là một danh sách con chứa các danh sách con phức hợp khác, thành một danh sách phẳng là danh [r]

(1)

95 CHƯƠNG

Cấu trúc danh sách

Chương trình bày khái niệm danh sách, cấu trúc đơn giản thơng dụng nhất, với chương trình tiêu biểu minh hoạ cách vận dụng danh sách Prolog Cấu trúc danh sách tạo nên môi trường lập trình thuận tiện ngơn ngữ Prolog

I. Biểu diễn cấu trúc danh sách

Danh sách kiểu cấu trúc liệu sử dụng rộng rãi ngơn ngữ lập trình phi số Một danh sách dãy đối tượng Khác với kiểu liệu tập hợp, đối tượng danh sách trùng (xuất nhiều lần) vị trí xuất đối tượng có ý nghĩa

Danh sách cách diễn đạt ngắn gọn kiểu liệu hạng phức hợp Prolog Hàm tử danh sách dấu chấm “.” Do việc biểu diễn danh sách hàm tử tạo biểu thức mập mờ, xử lý danh sách gồm nhiều phần tử lồng nhau, Prolog quy ước đặt dãy phần tử danh sách cặp móc vng

Chẳng hạn (a,.(b,[ ])) Là danh sách [ a, b ]

Danh sách phần tử anne, tennis, tom, skier (tên người) viết : [ anne, tennis, tom, skier ]

chính hàm tử :

( anne, .( tennis, .( tom, .( skier, [ ] ) ) ) )

Cách viết dạng cặp móc vng xuất bên danh sách Như thấy mục trước, đối tượng cấu trúc Prolog có biểu diễn Danh sách không nằm ngoại lệ, có cấu trúc

Làm cách để biểu diễn danh sách đối tượng Prolog chuẩn ? Có hai khả xảy danh sách rỗng khơng Nếu danh sách rỗng, viết dạng nguyên tử :

(2)

Lập trình lơgic Prolog 96 Nếu danh sách khác rỗng, xem cấu trúc từ hai thành phần (pair syntax) :

1 Thành phần thứ nhất, gọi đầu (head) danh sách

2 Thành phần thứ hai, phần lại danh sách (trừ phần đầu), gọi đuôi (tail) danh sách, danh sách

Trong ví dụ đầu anne, cịn đuôi danh sách : [ tennis, tom, skier ]

Nói chung, đầu danh sách đối tượng Prolog, biến, đuôi phải danh sách Hình I.1 Biểu diễn dạng danh sách mơ tả cấu trúc danh sách cho :

Hình I.1 Biểu diễn dạng danh sách

Vì tail danh sách, nên tail rỗng, lại tạo thành từ đầu head đuôi tail khác

Chú ý danh sách rỗng xuất số hạng, phần tử cuối xem danh sách gồm phần tử có phần danh sách rỗng:

[ skier ]

Ví dụ minh hoạ nguyên lý cấu trúc liệu tổng quát Prolog áp dụng cho danh sách có độ dài tuỳ ý

?- L1 = [ a, b, c ]. ?- L2 = [ a, a, a ]. L1 = [ a, b, c ] L2 = [ a, a, a ]

?- Leisure1 = [ tennis, music, [ ] ]. ?- Leisure2 = [ sky, eating ],

?- L = [ anne, Leisure1, tom, Leisure2 ] Leisure1 = [ tennis, music ]

Leisure2 = [ sky, eating ]

L = [ anne, [ tennis, music ], tom, [ sky, eating ] ]

anne đuôi danh sách đầu tennis

tom

(3)

Cấu trúc danh sách 97 Như vậy, phần tử danh sách đối tượng có kiểu bất kỳ, kể kiểu danh sách Thông thường, người ta xử lý đuôi danh sách danh sách Chẳng hạn, danh sách :

L = [ a, b, c ] viết :

tail = [ b, c ] L = .(a, tail)

Để biểu diễn danh sách tạo thành từ đầu (Head) đuôi (Tail), Prolog sử dụng ký hiệu | (split) để phân cách phần đầu phần đuôi sau :

L = [ a | Tail ]

Ký hiệu | dùng cách tổng quát cách viết số phần tử tuỳ ý danh sách trước | danh sách phần tử lại Danh sách viết lại sau :

[ a, b, c ] = [ a | [ b, c ] ] = [ a, b | [ c ] ] = [ a, b, c | [ ] ]

Sau số cách viết danh sách :

Kiểu hai thành phần Kiểu liệt kê phần tử

[ ] [ ]

[ a | [ ] ] [ a ]

[ a | b | [ ] ] [ a, b ]

[ a | X ] [ a | X ]

[ a | b | X ] [ a, b | X ]

[ X1 | [ [ Xn | [ ] ] ] ] [ X1, , Xn ] Ta định nghĩa danh sáchtheo kiểu đệ quy sau : List [ ]

List [ Element | List ]

II. Một số vị từ xử lý danh sách Prolog

SWI-Prolog có sẵn số vị từ xử lý danh sách sau :

Vị từ Ý nghĩa

append(List1, List2,

List3) Ghép hai danh sách

List1 List2 thành List3

member(Elem, List)

Kiểm tra Elem có phần tử danh sách List hay

không, nghĩa Elem hợp với

phần tử List

nextto(X, Y, List) Kiểm tra phần tử Y có đứng sau phần tử X

(4)

Lập trình lơgic Prolog 98

delete(List1, Elem, List2)

Xoá khỏi danh sách List1 phần tử hợp với Elemđể trả kết quảList2

select(Elem, List, Rest)

Lấy phần tửElem khỏi danh sách Listđể trả

những phần tử lại Rest, dùng để chèn phần tử vào danh sách

nth0(Index, List, Elem)

Kiểm tra phần tử thứ Index (tính từ 0) danh sách List có phải Elem hay không

nth1(Index, List, Elem)

Kiểm tra phần tử thứIndex (tính từ 1) danh sách List có phải Elem hay khơng

last(List, Elem) Kiểm tra phần tửđứng cuối danh sách List có phải Elem hay khơng

reverse(List1, List2)

Nghịch đảo thứ tự phần tử danh sách List1để

trả kết quảList2 permutation(List1,

List2) Hoán vị danh sách

List1 thành danh sách List2

flatten(List1, List2)

Chuyển danh sách List1 chứa phần tử

thành danh sách phẳng List2

Ví dụ : flatten([a, [b, [c, d], e]], X)

cho kết quảX = [a, b, c, d, e]

sumlist(List, Sum) Tính tổng phần tử của danh sách List chứa toàn

sốđể trả kết quảSum numlist(Low, High,

List)

Nếu Low High số cho Low =< High,

trả danh sách List = [Low, Low+1, , High]

Chú ý số vị từ xử lý danh sách sử dụng cho ràng buộc, kể tham đối biến

Trong Prolog, tập hợp biểu diễn danh sách, nhiên, thứ tự phần tử tập hợp không quan trọng, đối tượng dù xuất nhiều lần xem phần tử tập hợp Các phép tốn danh sách áp dụng cho tập hợp Đó :

• Kiểm tra phần tử có mặt danh sách tương tự việc kiểm tra phần tử có thuộc tập hợp khơng ?

• Ghép hai danh sách để nhận danh sách thứ ba tương ứng với phép hợp hai tập hợp

(5)

Cấu trúc danh sách 99 Prolog có sẵn số vị từ xử lý tập hợp sau :

Vị từ Ý nghĩa

is_set(Set) Kiểm tra Set có phải tập hợp hay không

list_to_set(List, Set)

Chuyển danh sách List thành tập hợp Set giữ

nguyên thứ tự phần tử List (nếu List có

phần tử trùng lấy phần tử gặp đầu tiên) Ví dụ : list_to_set([a,b,a], X) cho kết X = [a,b]

intersection(Set1,

Set2, Set3) Phép giao hai tập hợp

Set1 Set2 Set3

subtract(Set, Delete, Result)

Trả kết phép hiệu hai tập hợp Set Delete Result (là tập Set sau xoá hết

phần tử Delete có mặt đó)

union(Set1, Set2, Set3) Trả kết phép hợp hai tập hợp Set1 Set2 Set3

subset(Subset, Set) Kiểm tra tập hợp Subset có tập hợp Set

hay không

III. Các thao tác danh sách III.1.Xây dựng lại số vị từ có sẵn

Sau ta trình bày số thao tác danh sách cách xây dựng lại số vị từ có sẵn Prolog

III.1.1. Kiểm tra phần tử có mặt danh sách Prolog kiểm tra phần tử có mặt danh sách sau : member(X, L)

trong đó, X phần tử L danh sách Đích member(X, L) thoả mãn X xuất L Ví dụ :

?- member( b, [ a, b, c ] ) Yes

?- member( b, [ a, [ b, c ] ] ) No

?- member( [ b, c], [ a, [ b, c ] ] ) Yes

(6)

Lập trình lôgic Prolog 100 Phần tử X thuộc danh sách L :

1 X đầu L,

2 X phần tử L

Ta viết hai điều kiện thành hai mệnh đề, mệnh đề thứ kiện đơn giản, mệnh đề thứ hai luật :

member( X, [ X | Tail ] )

member( X, [ Head | Tail ] ) :- member( X, Tail ) :

member(X, [X|T])

member(X, [_|T]) :- member(X, T)

III.1.2. Ghép hai danh sách

Để ghép hai danh sách, Prolog có hàm : append( L1, L2, L3)

trong đó, L1 L2 hai danh sách, L3 danh sách kết phép ghép L1 L2 Ví dụ :

?- append( [ a, b ], [ c, d ], [ a, b, c, d ] ) Yes

?- append( [ a, b ], [ c, d ], [ a, b, a, c ] ) No

Hình III.1 Ghép hai danh sách [ X | L1 ] L2 thành [ X | L3 ]

Hàm append hoạt động phụ thuộc tham đối L1 theo cách sau : Nếu tham đối danh sách rỗng, tham đối thứ hai thứ ba phải

là danh sách nhất, gọi L Ta viết Prolog sau : append( [ ], L, L)

2 Nếu tham đối append danh sách khác rỗng, gồm đầu đuôi sau

[ X | L1 ]

X L3

X L1 L2

[ X | L1 ]

L3

(7)

Cấu trúc danh sách 101 Kết phép ghép danh sách danh sách [ X | L3 ], với L3 phép ghép L1 L2 Ta viết Prolog sau :

append( [ X | L1 ], L2, [ X | L3 ] ) :- append( L1, L2, L3 )

Hình 4.2 minh hoạ phép ghép hai danh sách [ X | L1 ] L2 Ta có ví dụ sau :

?- append( [ a, b, c ], [ 1, 2, ], L ) L = [ a, b, c, 1, 2, ]

?- append( [ a, [ b, c ], d ], [ a, [ ], b ], L ] ) L = [ a, [ b, c ], d, a, [ ], b ]

Thủ tục append sử dụng mềm dẻo theo nhiều cách khác

Chẳng hạn Prolog đưa bốn phương án để phân tách danh sách cho thành hai danh sách sau :

?- append( L1, L2, [ a, b, c ] ) L1 = [ ]

L2 = [ a, b, c ]; L1 = [ a ]

L2 = [ b, c ]; L1 = [ a, b ] L2 = [ c ];

L1 = [ a, b, c ] L2 = [ ];

Yes

Sử dụng append, ta tìm kiếm số phần tử danh sách Chẳng hạn, từ danh sách tháng năm, ta tìm tháng đứng trước tháng cho, giả sử tháng năm (May) :

?- append( Before, [ May | After ] ,

[ jan, fev, mar, avr, may, jun, jul, aut, sep, oct, nov, dec ] ).

Before = [ jan, fev, mar, avr ]

After = [ jun, jul, aut, sep, oct, nov, dec ] Yes

Tháng đứng trước tháng đứng sau tháng năm nhận sau : ?- append( _, [ Month1, may, Month2 | _ ] ,

(8)

Lập trình lơgic Prolog 102 Month1 = avr

Month2 = jun Yes

Bây cho trước danh sách :

L1 = [ a, b, z, z, c, z, z, z, d, e ]

Ta cần xóa phần tử đứng sau ba chữ z liên tiếp, kể ba chữ z : ?- L1 = [ a, b, z, z, c, z, z, z, d, e ],

append( L2, [ z, z, z | _ ], L1 ). L1 = [ a, b, z, z, c, z, z, z, d, e ] L2 = [ a, b, z, z, c ]

Hình III.2 Thủ tục member1 tìm đối tượng danh sách cho Trước ta định nghĩa quan hệ member( X, L ) để kiểm tra phần tử X có mặt danh sách L không Bây cách sử dụng append, ta định nghĩa lại member sau :

member1( X, L ) :- append( L1, [ X | L2], L) member1( b, [ a, b, c ]

)

Mệnh đề append So khớp : L1 = [ X | L1 ]

[ b | L2 ] = L2

[ a, b, c ] = [ X | L3 ]

Từđó kéo theo : X = a, L3 = [ b, c ]

Mệnh đề append So khớp :

L1 = [ ]

[ b | L2 ] = [ b, c ] Từđó kéo theo : L2 = [ c ]

thành công

Mệnh đề append So khớp :

L1 = [ ]

[ b | L2 ] = [ a, b, c ] Thất bại b ≠ a

append( L1, [ b | L2 ], [ a, b, c ] )

(9)

Cấu trúc danh sách 103 Mệnh đề có nghĩa : X có mặt danh sách L L phân tách thành hai danh sách, với X đầu danh sách thứ hai Định nghĩa member1 hoàn toàn tương đương với định nghĩa member

Ở ta sử dụng hai tên khác để phân biệt hai cách cài đặt Prolog Ta định nghĩa lại member1 cách sử dụng biến nặc danh (anonymous variable) :

member1( X, L ) :

-append( _ , [ X | _ ], L)

So sánh hai cách cài đặt khác quan hệ thành viên, ta nhận thấy nghĩa thủ tục định nghĩa member thể rõ :

Trong member, để kiểm tra phần tử X có mặt danh sách L không, Trước tiên kiểm tra phần tử đầu L đồng với X, khơng, Kiểm tra X có mặt phần đuôi L

Nhưng trường hợp định nghĩa member1, ta thấy hồn tồn nghĩa khai báo mà khơng có nghĩa thủ tục

Để hiểu cách member1hoạt động nào, ta xem xét trình Prolog thực câu hỏi :

?- member1( b, [ a, b, c ] )

Cách tìm thủ tục member1 đây tương tự member, cách duyệt phần tử, tìm thấy đối tượng cần tìm, danh sách cạn

III.1.3. Bổ sung phần tử vào danh sách

Phương pháp đơn giản để bổ sung phần tử vào danh sách đặt vị trí đầu tiên, để trở thành đầu Nếu X đối tượng mới, L danh sách cần bổ sung thêm, danh sách kết :

[ X | L ]

Người ta không cần viết thủ tục để bổ sung phần tử vào danh sách Bởi việc bổ sung biểu diễn dạng kiện cần :

insert( X, L, [ X | L ] )

III.1.4. Loại bỏ phần tử khỏi danh sách

Để loại bỏ phần tử X khỏi danh sách L, người ta xây dựng quan hệ : remove( X, L, L1 )

trong đó, L1 đồng với L, sau X bị loại bỏ khỏi L Thủ tục remove có cấu trúc tương tự member Ta lập luận sau

(10)

Lập trình lơgic Prolog 104 Nếu khơng, tìm cách loại bỏ X khỏi phần danh sách

remove( X, [ X | Tail ], Tail )

remove( X, [ Y | Tail ], [ Y | Tail1 ] ) : -remove( X, Tail, Tail1 )

Tương tự thủ tục member, thủ tục remove mang tính khơng xác định Nếu có nhiều phần tử X có mặt danh sách, remove xố phần tử nào, trình quay lui Tuy nhiên, lần thực hiện, remove xố phần tử X mà khơng đụng đến phần tử khác Ví dụ :

?- remove( a, [ a, b, a, a ], L ). L = [ b, a, a ];

L = [ a, b, a ]; L = [ a, b, a ] No

Thủ tục remove thất bại danh sách khơng chứa phần tử cần xố Người ta sử dụng remove khía cạnh khác, mục đích để bổ sung phần tử vào đâu danh sách

Ví dụ, ta muốn đặt phần tử a vào vị trí danh sách [ 1, 2, ], cần đặt câu hỏi : Cho biết danh sách L sau xoá a, ta nhận danh sách [ 1, 2, ] ?

?- remove( a, L, [ 1, 2, ] ). L = [ a, 1, 2, ];

L = [ 1, a, 2, ]; L = [ 1, 2, a, ]; L = [ 1, 2, 3, a ] No

Một cách tổng quát, phép toán chèn insert phần tử X vào danh sách List định nghĩa thủ tục remove cách sử dụng danh sách lớn LargerList làm tham đối thứ hai :

insert( X, List, LargerList ) : -remove( X, LargerList, List )

Ta định nghĩa quan hệ thuộc thủ tục member1 cách sử dụng thủ tục append Tuy nhiên, ta định nghĩa lại quan hệ thuộc thủ tục member2 thủ tục remove cách xem phần tử X thuộc danh sách List X bị xoá khỏi List :

member2( X, List ) :

(11)

Cấu trúc danh sách 105

III.1.5. Nghịch đảo danh sách

Sử dụng append, ta viết thủ tục nghịch đảo danh sách sau : reverse ( [ ], [ ] )

reverse ( [ X | Tail ], R ) : -reverse (Tail, R1 ), append(R1, [X], R)

?- reverse( [ a, b, c , d, e, f ] , L) L = [f, e, d, c, b, a]

Yes

Sau thủ tục khác để nghịch đảo danh sách có sử dụng hàm bổ trợ thân thủ tục :

revert(List, RevList) :-

rev(List, [ ], RevList) rev([ ], R, R)

rev([H|T], S, R) :- rev(T, [H|S], R)

?- revert( [ a, b, c , d, e, f ] , R) R = [f, e, d, c, b, a]

Yes

Sử dụng reverse, ta kiểm tra danh sách có đối xứng (palindrome) hay không :

palindrome(L) :-

reverse( L, L )

?- palindrome([ a, b, c , d, c, b, a ]) Yes

III.1.6. Danh sách

Ta xây dựng thủ tục sublist nhận hai tham đối hai danh sách L S cho S danh sách L sau :

?- sublist( [ c, d, e ], [ a, b, c , d, e, f ] ) Yes

?- sublist( [ c, e ], [ a, b, c , d, e, f ] ) No

(12)

Lập trình lơgic Prolog 106

Hình III.3 Các quan hệmember sublist

Quan hệ danh sách mô tả sau : S danh sách L :

1 Danh sách L phân tách thành hai danh sách L1 L2, Danh sách L2 phân tách thành hai danh sách S L3

Như thấy, việc phân tách danh sách mơ tả quan hệ ghép append

Do ta viết lại Prolog sau : sublist( S, L ) :

-append( L1, L2, L ), -append( S, L3, L2 ).

Ta thấy thủ tục sublist mềm dẻo sử dụng theo nhiều cách khác Chẳng hạn ta liệt kê danh sách danh sách cho sau :

?- sublist( S, [ a, b, c ] ) S = [ ];

S = [ a ]; S = [ a, b ]; S = [ a, b, c ]; S = [ b ];

III.2.Hốn vị

Đơi khi, ta cần tạo hoán vị danh sách Ta xây dựng quan hệ permutation có hai tham biến hai danh sách, mà danh sách hoán vị danh sách Ta tận dụng phép quay lui sau :

?- permutation( [ a, b, c ], P ) P = [ a, b, c ];

P = [ a, c, b ];

L

X L2

L1

[ X | L2 ] L

L2

L1 S L3

member( X, L )

(13)

Cấu trúc danh sách 107 P = [ b, a, c ];

Nguyên lý hoạt động thủ tục swap dựa hai trường hợp phân biệt, tuỳ theo danh sách thứ :

1 Nếu danh sách thứ rỗng, danh sách thứ hai phải rỗng

2 Nếu danh sách thứ khác rỗng, có dạng [ X | L ]và tiến hành hoán vị sau : trước tiên hoán vị L để nhận L1, sau chèn X vào tất vị trí L1

Hình III.4 Một cách xây dựng hoán vịpermutation danh sách [ X | L ]

Ta nhận hai mệnh đề tương ứng với thủ tục sau : permutation( [ ], [ ] ).

permutation( [ X | L ], P ) :

-permutation( L, L1 ), insert( X, L1, P ).

Một phương pháp khác loại bỏ phần tử X khỏi danh sách đầu tiên, hoán vị phần lại danh sách để nhận danh sách P, sau thêm X vào phần đầu P Ta có chương trình khác permutation2 sau :

permutation2( [ ], [ ] ).

permutation2( L, [ X | P ] ) :

-remove( X, L, L1 ), permutation2( L1, P ).

Từ đây, ta khai thác thủ tục hốn vị, chẳng hạn (chú ý chạy Arity Prolog cần gõ vào dấu chấm phẩy ; sau ->) :

?- permutation( [ red, blue, green ], P ) P = [ red, blue, green ];

P = [ red, green, blue ]; P = [ blue, red, green ]; P = [ blue, green, red ]; P = [ green, red, blue ]; P = [ green, blue, red ]; Yes

Hoặc sử dụng permutation theo cách khác sau : L1 hoán vị L

Chèn X vị tríđể nhận hoán vị [ X | L ]

X L

(14)

Lập trình lơgic Prolog 108 ?- permutation( L, [ a, b, c ] )

Prolog ràng buộc liên tiếp cho L để đưa hốn vị khác Tuy nhiên, NSD yêu cầu giải pháp khác, Prolog không trả lời “No”, mà rơi vào vịng lặp vơ hạn phải tìm kiếm hốn vị mà thực khơng tồn Trong trường hợp này, thủ tục permutation2 tìm thấy hốn vị thứ nhất, sau rơi vào vịng lặp vơ hạn Vì vậy, cần ý sử dụng quan hệ hoán vị

III.3.Một số ví dụ danh sách

III.3.1. Sắp xếp phần tử danh sách

Xây dựng thủ tục xếp phần tử có danh sách phương pháp chèn sau :

ins(X, [ ], [ X ])

ins(X, [H|T], [ X,H|T ]) :- X @=< H

ins(X, [ H|T ], [ H|L ]) :- X @> H, ins( X, T, L )

?- ins(8, [ 1, 2, 3, 4, ], L) L = [1, 2, 3, 4, 5, 8]

Yes

?- ins(1, L, [ 1, 2, 3, 4, ]) L = [2, 3, 4, 5]

Yes

ins_sort([ ], [ ]) ins_sort([H|T], L) :-

ins_sort(T, L1), ins(H, L1, L)

?- ins_sort([3, 2, 6, 4, 7, 1], L) L = [1, 2, 3, 4, 6, 7]

Yes

III.3.2. Tính độ dài danh sách

Xây dựng thủ tục tính độ dài hay đếm số lượng phần tử có mặt danh sách cho sau :

(15)

Cấu trúc danh sách 109 Nếu danh sách rỗng, độ dài N =

2 Nếu danh sách khác rỗng, tạo thành từ danh sách có dạng : [ head | queue ]

và có độ dài cộng với độ dài queue Ta có chương trình Prolog sau :

length( [ ], ).

length( [ _ | Queue ], N ) :- length(Queue, N1 ), N is + N1.

Kết chạy Prolog sau :

?- length( [ a, b, c, d, e ], N ). N =

Yes

?- length( [ a, [ b, c ], d, e ], N ). N =

Yes

Ta thấy mệnh đề thứ hai, hai đích phần thân khơng thể hốn đổi cho nhau, N1 phải ràng buộc trước thực đích :

N is + N1

Chẳng hạn, gọi trace, trình thực length( [ 1, 2, ], N ) sau :

(0) gọi length([1, 2, 3], N) -> (1) gọi length([2, 3], N’) -> (2) gọi length([3], N’’) ->

(3) gọi length([ ], N’’’) -> N’’’ = (4) gọi N’’ is + -> N’’ =

(5) gọi N’ is + -> N’ = (6) gọi N is + -> N =

Với is, ta đưa vào quan hệ nhạy cảm với thứ tự thực đích, khơng thể bỏ qua yếu tố thủ tục chương trình

Điều xảy ta không sử dụng is chương trình Chẳng hạn : length1( [ ], ).

length1( [ _ | Queue ], N ) :- length1( Queue, N1 ), N = + N1.

Lúc này, gọi :

(16)

Lập trình lơgic Prolog 110 Prolog trả lời :

N = + (1 + (1 + (1 + 0))) Yes

Phép cộng không khởi động cách tường minh nên không thực Tuy nhiên, ta hốn đổi hai đích mệnh đề thứ hai length1 :

length1( [ ], )

length1( [ _ | Queue ], N ) :- N = + N1,

length1( Queue, N1 ).

Kết chạy chương trình sau hốn đổi y hệt cũ Bây giờ, ta lại rút gọn mệnh đề cịn đích :

length1( [ ], )

length2( [ _ | Queue ], + N ) :- length2( Queue, N ).

Kết chạy chương trình lần y hệt cũ Prolog không đưa trả lời mong muốn, mà :

?- length1([ a, b, c, d], N) N = 1+ (1+ (1+ (1+0)))

Yes

III.3.3. Tạo sinh số tự nhiên

Chương trình sau tạo sinh liệt kê số tự nhiên : % Natural Numbers

nat(0)

nat(N) :- nat(M), N is M + Khi thực đích câu hỏi : ?- nat(N), write(N), nl, fail

(17)

Cấu trúc danh sách 111

Tóm tắt chương

• Danh sách cấu trúc rỗng, gồm hai phần : phần đầu phần tử phần cịn lại danh sách

• Prolog quản lý danh sách theo cấu trúc nhị phân Prolog cho phép sử dụng nhiều cách khác để biểu diễn danh sách

[ Object1, Object2, ] [ Head | Tail ]

hoặc [ Object1, Object2, | Others ] Với Tail Others danh sách

• Các thao tác cổ điển danh sách lập trình : kiểm tra phần tử có thuộc danh sách cho trước không, phép ghép hai danh sách, bổ sung loại bỏ phần tử đầu cuối danh sách, trích danh sách

Bài tập chương

1 Viết thủ tục sử dụng append để xóa ba phần tử cuối danh sách L, tạo danh sách L1 Hướng dẫn : L phép ghép L1 với danh sách ba phần tử (đã bị xóa khỏi L)

2 Viết dãy đích để xóa ba phần tử ba phần tử cuối danh sách L, để trả danh sách L2

3 Định nghĩa quan hệ :

last_element( Object, List )

sao cho Object phải phần tử cuối danh sách List Hãy viết thành hai mệnh đề, có mệnh đề sử dụng append, mệnh đề không sử dụng append

4 Định nghĩa hai vị từ :

even_length( List )và odd_length( List )

được thõa mãn số phân tử danh sách List chẵn hay lẻ tương ứng Ví dụ danh sách :

[ a, b, c, d ] có độ dài chẵn, [ a, b, c ] có độ dài lẽ

5 Cho biết kết Prolog trả lời câu hỏi sau : ?- [1,2,3] = [1|X]

(18)

Lập trình lơgic Prolog 112

?- [1 | [2,3]] = [1,2,X] ?- [1 | [2,3,4]] = [1,2,X] ?- [1 | [2,3,4]] = [1,2|X] ?- b(o,n,j,o,u,r) = L ?- bon(Y) = [X,jour] ?- X(Y) = [bon,jour]

6 Viết chương trình Prolog kiểm tra danh sách có phải tập hợp danh sách khác khơng ? Chương trình hoạt động sau :

?- subset2([4,3],[2,3,5,4]) Yes

7 Viết chương trình Prolog để lấy phần tử từ danh sách Chương trình chèn phần tử vào danh sách hoạt động sau : ?- takeout(3,[1,2,3],[1,2])

Yes

?- takeout(X,[1,2,3],L) X =

L = [2, 3] ; X =

L = [1, 3] ; X =

L = [1, 2] ; No

?- takeout(4,L,[1,2,3])

L = [4, 1, 2, 3] ; L = [1, 4, 2, 3] ; L = [1, 2, 4, 3] ; L = [1, 2, 3, 4] ; No

8 Viết vị từ Prolog getEltFromList(L,N,E) cho phép lấy phần tử thứ N danh sách Thất bại danh sách khơng có đủ N phần tử Chương trình hoạt động sau :

?- getEltFromList([a,b,c],0,X) No

?- getEltFromList([a,b,c],2,X) X = b

(19)

Cấu trúc danh sách 113 Viết chương trình Prolog tìm phần tử lớn phần tử nhỏ

danh sách số Chương trình hoạt động sau : ?- maxmin([3,1,5,2,7,3],Max,Min) Max =

Min = Yes

?- maxmin([2],Max,Min) Max =

Min = Yes

10 Viết chương trình Prolog chuyển danh sách phức hợp, danh sách mà phần tử danh sách chứa danh sách phức hợp khác, thành danh sách phẳng danh sách chứa phần tử tất danh sách có thể, giữ nguyên thứ tự lúc đầu Chương trình hoạt động sau :

flatten([[1,2,3],[4,5,6]], Flatlist) Flatlist = [1,2,3,4,5,6]

Yes

flatten([[1,[hallo,[[aloha]]],2,[],3],[4,[],5,6]], Flatlist)

Flatlist = [1, hallo, aloha, 2, 3, 4, 5, 6] Yes

11 Viết chương trình Prolog thực vị từ xử lý tập hợp cho phần lý thuyết (mục II)

12 Sử dụng vị từ forall để viết chương trình Prolog kiểm tra hai danh sách có rời (disjoint) khơng ? Chương trình hoạt động sau :

?- disjoint([a,b,c],[d,g,f,h]) Yes

?- disjoint([a,b,c],[f,a]) No

13 Vị từ forall(Cond, Action) thực kiểm tra so khớp tương ứng Cond, thường kết hợp với vị từ member, Action Ví dụ kiểm tra việc thực phép toán số học danh sách L đắn ?- forall(member(Result = Formula, [2 = + 1, = * 2]), Result =:= Formula)

(20)

Lập trình lôgic Prolog 114 14 Sử dụng vị từ forall để viết chương trình Prolog kiểm tra danh sách

có tập hợp danh sách khác hay khơng ? Chương trình hoạt động sau :

?- subset3([a,b,c],[c,d,a,b,f]) Yes

?- subset3([a,b,q,c],[d,a,c,b,f]) No

15 Sử dụng vị từ append ghép hai danh sách để viết chương trình Prolog thực việc sau :

prefixe(L1, L2) danh sách L1 đứng trước (prefixe list) danh sách L2 suffixe(L1, L2) danh sách L1 đứng sau (suffixe list) danh sách L2 isin(L1, L2) phần tử danh sách L1 có mặt danh sách L2 16 Sử dụng phương pháp Quicksort viết chương trình Prolog xếp nhanh

danh sách số cho theo thứ tự tăng dần

17 Đọc hiểu chương trình sau dựng lại thuật toán : /* Missionarys & Cannibals */

/* Tránh vòng lặp */ lNotExist(_,[])

lNotExist(X,[T|Q]) :-

X\==T, lNotExist(X,Q) /* Kiểm tra tính hợp lý trạng thái */ valid(MG,CG,MD,CD) :-

MG>=0, CG>=0, MD>=0, CD>=0, MG=0, MD>=CD valid(MG,CG,MD,CD) :-

MG>=0, CG>=0, MD>=0, CD>=0, MG>=CG, MD=0 valid(MG,CG,MD,CD) :-

MG>=0, CG>=0, MD>=0, CD>=0, MG>=CG, MD>=CD /* Xây dựng cung kiểm tra */

sail(1,0) sail(0,1) sail(1,1) sail(2,0) sail(0,2) arc([left,MGi,CGi,MDi,CDi],[droite,MGf,CGf,MDf,CDf]) :-

sail(Mis,Can),

MGf is MGi-Mis, MDf is MDi+Mis, CGf is CGi-Can, CDf is CDi+Can, valid(MGf,CGf,MDf,CDf)

arc([right,MGi,CGi,MDi,CDi],[left,MGf,CGf,MDf,CDf]) :- sail(Mis,Can),

MGf is MGi+Mis, MDf is MDi-Mis, CGf is CGi+Can, CDf is CDi-Can, valid(MGf,CGf,MDf,CDf)

Ngày đăng: 10/03/2021, 14:19

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN