Giáo trình lập trình logic trong prolog phần 2 NXB đại học quốc gia

20 230 0
Giáo trình lập trình logic trong prolog  phần 2   NXB đại học quốc gia

Đ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

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 ] 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ử : [ ] 95 96 Lập trình lơgic Prolog Nếu danh sách khác rỗng, xem cấu trúc từ hai thành phần (pair syntax) : Thành phần thứ nhất, gọi đầu (head) danh sách 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, danh sách : [ tennis, tom, skier ] Nói chung, đầu danh sách đối tượng Prolog, biến, 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 : anne đầu đuôi danh sách tennis tom skier [] 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 đuôi 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 L2 ???- L1 = [ a, b, c ] L2 = [ a, a, a ] = [ a, b, c ] = [ 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 ] ] 97 Cấu trúc danh sách 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ừ append(List1, List2, List3) member(Elem, List) nextto(X, Y, List) Ý nghĩa Ghép hai danh sách List1 List2 thành List3 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 Kiểm tra phần tử Y có đứng sau phần tử X danh sách List hay không 98 Lập trình lơgic Prolog delete(List1, Elem, List2) select(Elem, List, Rest) nth0(Index, List, Elem) nth1(Index, List, Elem) last(List, Elem) reverse(List1, List2) permutation(List1, List2) flatten(List1, List2) sumlist(List, Sum) numlist(Low, High, List) Xoá khỏi danh sách List1 phần tử hợp với Elem để trả kết List2 Lấy phần tử Elem khỏi danh sách List để trả phần tử lại Rest, dùng để chèn phần tử vào danh sách Kiểm tra phần tử thứ Index (tính từ 0) danh sách List có phải Elem hay khơng Kiểm tra phần tử thứ Index (tính từ 1) danh sách List có phải Elem hay khơng Kiểm tra phần tử đứng cuối danh sách List có phải Elem hay không Nghịch đảo thứ tự phần tử danh sách List1 để trả kết List2 Hoán vị danh sách List1 thành danh sách 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 X = [a, b, c, d, e] Tính tổng phần tử danh sách List chứa toàn số để trả kết Sum 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 toá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 Thêm phần tử mới, hay loại bỏ phần tử 99 Cấu trúc danh sách Prolog có sẵn số vị từ xử lý tập hợp sau : Vị từ is_set(Set) Ý nghĩa Kiểm tra Set có phải tập hợp hay khơng 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ó list_to_set(List, Set) 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) đó, 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 Từ kết trên, ta giải thích quan hệ member(X, L) sau : 100 Lập trình lơgic Prolog Phần tử X thuộc danh sách L : X đầu L, 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) đó, 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 [ X | L1 ] X L1 L2 L3 X L3 [ X | L3 ] 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 danh sách nhất, gọi L Ta viết Prolog sau : append( [ ], L, L) Nếu tham đối append danh sách khác rỗng, gồm đầu sau [ X | L1 ] 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 | _ ] , [ jan, fev, mar, avr, may, jun, jul, aut, sep, oct, nov, dec ] ) 102 Lập trình lơgic Prolog 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 ] member1( b, [ a, b, c ] ) append( L1, [ b | L2 ], [ a, b, c ] ) Mệnh đề append Mệnh đề append So khớp : L1 = [ ] [ b | L2 ] = [ a, b, c ] Thất bại b ≠ a So khớp : L1 = [ X | L1’ ] [ b | L2 ] = L2’ [ a, b, c ] = [ X | L3’ ] Từ kéo theo : X = a, L3’ = [ b, c ] append( L1’, [ b | L2 ], [ b, c ] ) Mệnh đề append So khớp : L1’ = [ ] [ b | L2 ] = [ b, c ] Từ kéo theo : L2 = [ c ] thành cơng 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) 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 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 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 ) đó, 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 Nếu phần tử X đầu danh sách, kết danh sách 104 Lập trình lơgic Prolog 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 xoá phần tử X mà không đụng đến phần tử khác Ví dụ : ?- remove( a, L = [ b, a, a L = [ a, b, a L = [ a, b, a No [ a, b, a, a ], L ) ]; ]; ] 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, L = [ a, 1, 2, L = [ 1, a, 2, L = [ 1, 2, a, L = [ 1, 2, 3, a No [ 1, 2, ] ) ]; ]; ]; ] 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 ) :remove( X, List, _ ) 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 Nguyên lý để xây dựng thủ tục sublist tương tự thủ tục member1, quan hệ danh sách tổng qt 106 Lập trình lơgic Prolog L L1 L1 X L2 L [ X | L2 ] S member( X, L ) L3 sublist( S, L ) L2 Hình III.3 Các quan hệ member sublist Quan hệ danh sách mô tả sau : S danh sách L : 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 Hoán vị Đơi khi, ta cần tạo hố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 ]; 107 Cấu trúc danh sách 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ứ : Nếu danh sách thứ rỗng, danh sách thứ hai phải rỗng Nếu danh sách thứ khác rỗng, có dạng [ X | L ] tiến hành hoán vị sau : trước tiên hốn vị L để nhận L1, sau chèn X vào tất vị trí L1 X L hoán vị L L1 L1 hoán vị L Chèn X vị trí để nhận hoán vị [ X | L ] Hình III.4 Một cách xây dựng hố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 hoá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 : 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 : length( L, N ) Xảy hai trường hợp : 109 Cấu trúc danh sách Nếu danh sách rỗng, độ dài N = Nếu danh sách khác rỗng, tạo thành từ danh sách có dạng : [ head | queue ] 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) (1) (2) (3) (4) (5) (6) gọi gọi gọi gọi gọi gọi gọi length([1, 2, 3], N) -> length([2, 3], N’) -> length([3], N’’) -> length([ ], N’’’) -> N’’’ = N’’ is + -> N’’ = N’ is + -> N’ = N is + -> N = Với is, ta đưa vào quan hệ nhạy cảm với thứ tự thực đích, 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 : ?- length1( [ a, [ b, c ], d, e ], N ) 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 đề đí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 số tự nhiên tạo sinh liên tiếp nhờ kỹ thuật quay lui Sau số tự nhiên nat(N) in nhờ write(N), fail bắt buộc thực quay lui Khi đó, luật thứ hai vận dụng để tạo sinh số tự nhiên tiếp tục NSD định dừng chương trình (^C) 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 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 ] [ 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 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) 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 Định nghĩa quan hệ : last_element( Object, List ) 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 Định nghĩa hai vị từ : even_length( List ) odd_length( List ) 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ẽ Cho biết kết Prolog trả lời câu hỏi sau : ?- [1,2,3] = [1|X] ?- [1,2,3] = [1,2|X] 112 Lập trình lơgic Prolog ??????- [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] 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 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 = L = L = L = No [4, [1, [1, [1, 1, 4, 2, 2, 2, 2, 4, 3, 3] 3] 3] 4] ; ; ; ; 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 ?- getEltFromList([a,b,c],4,X) No 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) Result = _G615 Formula = _G616 Yes 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 tố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) /* Phép đệ quy */ ... Cho biết kết Prolog trả lời câu hỏi sau : ?- [1 ,2, 3] = [1|X] ?- [1 ,2, 3] = [1 ,2| X] 1 12 Lập trình lơgic Prolog ??????- [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)... Elem có phần tử danh sách List hay không, nghĩa Elem hợp với phần tử List Kiểm tra phần tử Y có đứng sau phần tử X danh sách List hay không 98 Lập trình lơgic Prolog delete(List1, Elem, List2) select(Elem,...96 Lập trình lơgic Prolog Nếu danh sách khác rỗng, xem cấu trúc từ hai thành phần (pair syntax) : Thành phần thứ nhất, gọi đầu (head) danh sách Thành phần thứ hai, phần lại danh sách (trừ phần

Ngày đăng: 19/06/2019, 10:04

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan