[英]Reversing a List in Prolog
我已完成編程課的作業。 我應該創建一個反轉列表的Prolog程序。 但是,我很難理解為什么它的確有效。
%1. reverse a list
%[a,b,c]->[c,b,a]
%reverse(list, rev_List).
reverse([],[]). %reverse of empty is empty - base case
reverse([H|T], RevList):-
reverse(T, RevT), conc(RevT, [H], RevList). %concatenation
在這種情況下,RevT究竟是什么? 我知道它應該代表T的反向或給定列表的其余部分,但是我沒有看到它如何具有任何值,因為我沒有將它分配給任何東西。 它是否與RevList具有相同的用途,但對於每個遞歸調用?
另外,為什么我必須在我的conc()函數調用中使用[H]而不是H? H不是指列表的頭部(例如:[H])? 或者它只是引用列表頭部的項目(只是H)?
請幫我解決這個問題。 我正在努力理解這種編程背后的邏輯。
您的解決方案解釋說:如果我們反轉空列表,我們將獲得空列表。 如果我們反轉列表[H | T],我們最終得到通過反轉T並與[H]連接得到的列表。 要查看遞歸子句是否正確,請考慮列表[a,b,c,d]。 如果我們反轉這個列表的尾部,我們得到[d,c,b]。 將其與[a]連接得[d,c,b,a],這與[a,b,c,d]相反
另一個反向解決方
reverse([],Z,Z).
reverse([H|T],Z,Acc) :- reverse(T,Z,[H|Acc]).
呼叫:
?- reverse([a,b,c],X,[]).
欲了解更多信息,請閱讀: http : //www.learnprolognow.org/lpnpage.php?pagetype = html&pageid = lll -htmlse25
Prolog的列表是簡單的數據結構: ./2
[]
。 [a]
列表實際上是這個結構: .(a,[])
。 [a,b]
實際上是這種結構: .(a,.(b,[]))
[a,b,c]
實際上就是這樣的結構: .(a,.(b,.(c,[])))
方括號表示法是一種語法糖,可以讓你不必花費你的生命打字括號。 更不用說它在眼睛上更容易了。
由此,我們得到列表頭部的概念(最外層./2
結構中的數據)和列表的尾部 (子列表包含在最外層的./2
數據結構中)。
這基本上是您在C中看到的經典單鏈表的相同數據結構:
struct list_node
{
char payload ;
struct list_node *next ;
}
其中next
指針是NULL或其他列表結構。
那么,從那以后,我們得到了反向/ 2的簡單[天真]實現:
reverse( [] , [] ) . % the empty list is already reversed.
reverse[ [X] , [X] ) . % a list of 1 item is already reversed. This special case is, strictly speaking, optional, as it will be handled by the general case.
reverse( [X|Xs] , R ) :- % The general case, a list of length >= 1 , is reversed by
reverse(Xs,T) , % - reversing its tail, and
append( T , [X] , R ) % - appending its head to the now-reversed tail
. %
該算法可用於在更傳統的編程語言中反轉單鏈表。
但是,這種算法效率不高:對於初學者來說,它表現出O(n 2 )行為。 它也不是尾遞歸,這意味着足夠長度的列表將導致堆棧溢出。
應該注意的是,要將一個項目附加到一個prolog列表需要遍歷整個列表,由於prolog列表的結構,前置是一個簡單的操作。 我們可以將項目添加到現有列表中,如下所示:
prepend( X , Xs , [X|Xs] ) .
prolog中常見的習語是使用帶有累加器的worker謂詞 。 這使得reverse/2
實現更加高效,並且(可能)更容易理解。 在這里,我們通過將累加器設置為空列表來反轉列表。 我們遍歷源列表。 當我們在源列表中遇到項目時,我們將它添加到反向列表中,從而產生反向列表。
reverse(Xs,Ys) :- % to reverse a list of any length, simply invoke the
reverse_worker(Xs,[],Ys) . % worker predicate with the accumulator seeded as the empty list
reverse_worker( [] , R , R ). % if the list is empty, the accumulator contains the reversed list
reverse_worker( [X|Xs] , T , R ) :- % if the list is non-empty, we reverse the list
reverse_worker( Xs , [X|T] , R ) % by recursing down with the head of the list prepended to the accumulator
.
現在你有一個在O(n)時間運行的reverse/2
實現。 它也是尾遞歸的,這意味着它可以處理任何長度的列表而不會吹掉它的堆棧。
考慮使用DCG,這更容易理解:
reverse([]) --> [].
reverse([L|Ls]) --> reverse(Ls), [L].
例:
?- phrase(reverse([a,b,c]), Ls).
Ls = [c, b, a].
在這種情況下,RevT究竟是什么? 我知道它應該代表T的反向或給定列表的其余部分,但是我沒有看到它如何具有任何值,因為我沒有將它分配給任何東西。 它是否與RevList具有相同的用途,但對於每個遞歸調用?
Prolog中的變量是關系論證的“占位符”。 我們知道,在成功調用之后,正是指定的參數適用於該關系。
如果呼叫成功,則RevT
將具有值。 具體來說, 當列表不為空時 ,將是調用conc(RevT, [H], RevList)
的最后一個參數。 否則,將是空列表。
另外,為什么我必須在我的conc()函數調用中使用[H]而不是H? H不是指列表的頭部(例如:[H])? 或者它只是引用列表頭部的項目(只是H)?
是的,H指的是列表的第一個項目 (通常稱為元素 ),然后我們必須“重塑”它作為一個列表(只有1個元素),如conc / 3所要求的那樣,這是列表中的另一個關系。
只是關於測試 reverse/2
謂詞定義的注釋,太長而不適合評論。
反轉列表是引入QuickCheck的“hello world”示例,這意味着您可以使用它來幫助測試您的定義。 首先,我們定義一個為reverse/2
謂詞保留的屬性 :反轉列表兩次必須給出原始列表,我們可以將其轉換為:
same_list(List) :-
reverse(List, Reverse),
reverse(Reverse, ReverseReverse),
List == ReverseReverse.
使用Logtalk的lgtunit
工具lgtunit
實現:
% first argument bound:
| ?- lgtunit::quick_check(same_list(+list)).
% 100 random tests passed
yes
% both arguments unbound
| ?- lgtunit::quick_check(same_list(-list)).
% 100 random tests passed
yes
或者干脆:
% either bound or unbound first argument:
| ?- lgtunit::quick_check(same_list(?list)).
% 100 random tests passed
yes
但我們需要另一個屬性定義來測試第二個參數bound:
same_list_2(Reverse) :-
reverse(List, Reverse),
reverse(List, ListReverse),
Reverse == ListReverse.
我們現在可以這樣做:
% second argument bound:
| ?- lgtunit::quick_check(same_list_2(+list)).
% 100 random tests passed
yes
但請注意,這種基於屬性的/隨機化測試不會檢查非終止情況,因為這些僅在第一個解決方案后回溯時發生。
以下是reverse / 2的典型實現。 然而,它確實存在明顯低於“非終止”的問題。
?- ['/dev/tty'] .
reverse(_source_,_target_) :-
reverse(_source_,_target_,[]) .
reverse([],_target_,_target_) .
reverse([_car_|_cdr_],_target_,_collect_) :-
reverse(_cdr_,_target_,[_car_|_collect_]) .
end_of_file.
。
?- reverse([],Q) .
Q = []
?- reverse([a],Q) .
Q = [a]
?- reverse([a,b],Q) .
Q = [b,a]
?- reverse([a,b,c],Q) .
Q = [c,b,a]
?- reverse(P,[]) .
P = [] ? ;
%% non-termination ! %%
^CAction (h for help): a
?- reverse(P,[a]) .
P = [a] ? ;
%% non-termination ! %%
^CAction (h for help): a
?- reverse(P,[a,b]) .
P = [b,a] ? ;
%% non-termination ! %%
^CAction (h for help): a
?- reverse(P,[a,b,c]) .
P = [c,b,a] ? ;
%% non-termination ! %%
^CAction (h for help): a
:- op(2'1,'fy','#') .
:- op(2'1,'fy','##') .
:- op(2'1,'fy','###') .
/ *以下是我剛剛發明的reverse / 2的實現,它沒有遇到reverse(P,[])
的非終止問題。 * /
reverse(_source_,_target_) :-
reverse(_source_,_target_,_source_,_target_,[],[]) .
reverse(_source_,_target_,[],[],_target_,_source_) .
reverse(_source_,_target_,[_source_car_|_source_cdr_],[_target_car_|_target_cdr_],_source_collect_,_target_collect_) :-
reverse(_source_,_target_,_source_cdr_,_target_cdr_,[_source_car_|_source_collect_],[_target_car_|_target_collect_]) .
/*
?- reverse([],Q) .
Q = []
?- reverse([a],Q) .
Q = [a]
?- reverse([a,b],Q) .
Q = [b,a]
?- reverse([a,b,c],Q) .
Q = [c,b,a]
/*
?- reverse(P,[]) .
P = []
?- reverse(P,[a]) .
P = [a]
?- reverse(P,[a,b]) .
P = [b,a]
?- reverse(P,[a,b,c]) .
P = [c,b,a]
*/
/*
?- reverse(P,Q) .
P = Q = [] ? ;
P = Q = [_A] ? ;
P = [_A,_B],
Q = [_B,_A] ? ;
P = [_A,_B,_C],
Q = [_C,_B,_A] ? ;
P = [_A,_B,_C,_D],
Q = [_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E],
Q = [_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F],
Q = [_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G],
Q = [_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H],
Q = [_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I],
Q = [_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J],
Q = [_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K],
Q = [_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L],
Q = [_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M],
Q = [_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N],
Q = [_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O],
Q = [_O,_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P],
Q = [_P,_O,_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P,_Q],
Q = [_Q,_P,_O,_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P,_Q,_R],
Q = [_R,_Q,_P,_O,_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P,_Q,_R,_S],
Q = [_S,_R,_Q,_P,_O,_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P,_Q,_R,_S,_T],
Q = [_T,_S,_R,_Q,_P,_O,_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ? ;
P = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P,_Q,_R,_S,_T,_U],
Q = [_U,_T,_S,_R,_Q,_P,_O,_N,_M,_L,_K,_J,_I,_H,_G,_F,_E,_D,_C,_B,_A] ?
*/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.