2016-04-21 90 views
0

我正在研究一個將在列表上執行「交換」的序言算法。序言類型錯誤

實施例:

輸入:[1,2,3,4] - >輸出:[3,4,1,2] 輸入:[1,2,3,4,5] - >輸出:[4,5,3,1,2]

上半年和下半年的名單交換的地方,如果有一個奇數,然後中心元素保留它的位置。我想出了一個算法,但我得到一個錯誤:

?- swap([1,2,3,4],L). 
ERROR: length/2: Type error: `integer' expected, found `round(4/2)' 

我的代碼如下:

swap(L, S) :- 
    length(L, Length), 
    reverse(L, L2), 
    mixing(L, Length, A), 
    trim(L2, Length/2 , B), 
    append(A,B,S). 

trim(L,N,S) :- 
    length(P,N), 
    append(P,S,L). 

mixing(L, Length, A) :- 
    ( mod(Length, 2) == 0 
    -> trim(L, round(Length/2), A) 
    ; trim(L, round(Length/2), A) 
    ). 

的問題是,在「混合」當我打電話TRIM(L, round(長度/ 2),A)類型不是整數?我知道Length/2不是一個整數(很可能是一個浮點數),我認爲round等同於整數(expr),它將數據類型舍入並轉換爲整數。我也嘗試用truncate(expr)和integer(expr)替換round,但是我收到了相同的錯誤。有人能解釋我做錯了什麼嗎?

回答

1

round不是一個函數,它是一個謂詞。我沒有看過的代碼的其餘部分,但該行應該是

round(Length/2, R), trim(L, R, A) 

編輯:順便說一句,你這得太多。

swap([], []). 
swap([X], [X]). 
swap([X, Y | A], [Y, X | B]) :- swap(A, B). 

+1

您建議的'swap/2'實現並沒有做OP想要做的事情。 – lurker

+0

我是新來的prolog一般所以不能超清所有的措辭。感謝您對類型問題的澄清。我仍然堅持編程的必要心態。這是有道理的,我需要使用一個謂詞來獲取值,然後重新插入。還要感謝代碼建議。你能解釋最後一行嗎? –

+0

@ lurker:哦,的確不是!原來,我看不懂。 – Amadan

2

序言沒有做內聯表達式求值。因此,諸如trim(L2, Length/2 , B)trim(L, round(Length/2), A)之類的呼叫將無法按預期工作。只有在使用某些運算符(如is/2,算術比較或其CLP(FD)對應項)時纔會對錶達式進行特定評估。這些表達式需要按如下完成:L is Length // 2, trim(L2, L, B)R is round(Length/2), trim(L, R, A)如果按照字面意思完成。

但是,您的解決方案可能會縮減,如下所示。

swap(L, S) :- 
    same_length(L, S),    % L and S are necessarily the same length 
    length(L, N), 
    M is N // 2, % integer divide ; half the original list length 
    length(Left, M),     % Left is a list of half the length of L 
            % or half minus one if the length is odd 
    ( (N mod 2) =:= 1    % If the original length is odd... 
    -> append(Left, [H|Right], L), % then L is Left + [H|Right] 
     append(Right, [H|Left], S) % So, S is Right + [H|Left] 
    ; append(Left, Right, L),  % otherwise, L is Left + Right 
     append(Right, Left, S)  % So, S is Right + Left 
    ). 
+0

你能詳細說明這是如何工作的嗎?具體問題:在「length(Left,M)Left是從哪裏來的?在if/else塊中,我假設」N/1 =:= 1「是確定它是偶數還是奇數,儘管我沒有在附錄聲明中,我再一次不確定Left和Right從哪裏來,但是「[H |」是什麼意思? Right]「do/stand for? –

+1

@BrianSizemore'Left'是一個我編制的變量,如果你調用'length(Left,M)',那麼Prolog將找到那個查詢的解決方案,它的長度爲M,如果知道了M,Prolog就會生成一個沒有填充元素的列表,因此它會創建一個長度爲M的空列表,查看手冊中的算術運算符,所以是'N/\ 1'和一個1位的數字,我可以使用'mod',列表的符號'[H | T]代表一個列表,其中第一個元素(頭部)'H'和列表的其餘部分尾巴)'T'。'=:='比較表達式評估的結果。 – lurker

+1

@BrianSizemore我在代碼中添加了一些註釋,並將'/ \'更改爲'mod'以更清晰一些。在Prolog解釋器中,你可以用'append/3'來玩,看看你有什麼變量而不是實例化的列表。嘗試,例如,追加(A,B,[a,b,c])並看看它做了什麼。還可以嘗試一下,「長度(L,5)」來看看它做了什麼。 – lurker

0

慣用的解決方案:

swap(L,S) :- 
    %maplist(when_, [ 
    append([X,C,Y],L), (C=[];C=[_]), same_length(X,Y), 
    reverse(X,U), reverse(Y,V), append([U,C,V],S) 
    %]) 
    . 

?- swap([1,2,3,4,5],S). 
S = [2, 1, 3, 5, 4] ; 
false. 

它不是一個真正的關係,因爲它掛起如果調用模式swap(-,+),但它的行爲在取消對包圍,並提供該片段後更好:

:- meta_predicate when_(0). 
when_(P) :- 
    strip_module(P,_,Q), Q =.. [_|As], 
    or_list(As, Exp), display(Exp), 
    when(Exp, P). 

or_list([A], ground(A)) :- !. 
or_list([A|As], (ground(A);Exp)) :- or_list(As, Exp). 

編輯

@false'評論後,我試圖解釋這個答案的動機。所請求的swap/2'函數'似乎是展示Prolog數據模型特性的理想候選者,儘管請求的解釋(關於應用於列表的整數算術的適當句法用法)在這裏甚至沒有暗示。

從Prolog的一開始,我們就可以使用關係型功能(遞歸)工具的獨特混合,這對新手來說可能毫無意義。至少,這仍然讓我感到意外......我喜歡這個事實。

現在,功能邏輯編程,例如Curry,嘗試通過「縮小」的解決方案。從鏈接頁面:

Narrowing is useful because it allows a function to be treated as a relation: its value can be computed "in both directions"

現在,when_/1它是一個簡單的方法來解決同一個問題。腳踏實地,swap/2被描述爲一個函數,但可能是實現的作爲關係?

@false建議,將same_length(L,S)作爲第一個目標,修復swap( - ,+)模式,但在swap( - , - )上循環。基於when_/1的方法反而報告不能對連詞進行研究。除了

編輯

終止的問題,我選擇的目標爲了真的是無效的。暗示這個答案another question,我突然想到一個很大的效率增益(至少,對於模式互換(+, - ))可得將限制第一:

3 ?- numlist(1,100,L), time((append([X,C,Y],L), (C=[];C=[_]), same_length(X,Y), append([Y,C,X], S))). 
% 328,899 inferences, 0.125 CPU in 0.125 seconds (100% CPU, 2634422 Lips) 
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...], 
X = [1, 2, 3, 4, 5, 6, 7, 8, 9|...], 
C = [], 
Y = [51, 52, 53, 54, 55, 56, 57, 58, 59|...], 
S = [51, 52, 53, 54, 55, 56, 57, 58, 59|...] 
. 

4 ?- numlist(1,100,L), time(((C=[];C=[_]), same_length(X,Y), append([X,C,Y],L), append([Y,C,X], S))). 
% 3,273 inferences, 0.001 CPU in 0.001 seconds (100% CPU,999 Lips) 
+1

什麼時候交換/ 2是「真實」關係?你提到的情況很容易通過添加'same_length(L,S)'作爲第一個目標來解決。那麼它會是一個真正的關係嗎? – false

+0

@false:太困難了,我只是暗示了一些替代方案,OP可以在介紹性的潛伏者評論之後加以欣賞。當然,你的解決方案要好得多,當/ 2在整個連接點上傳播。你認爲我應該刪除這個答案,以防萬一混淆?謝謝 – CapelliC

+1

這可以作爲答案。我仍然想知道你真正的關係是什麼意思。畢竟,在修復之後,'swap(L,[a | L])'仍然沒有終止,儘管沒有解決方案(=有限多種解決方案)。 – false

0

下面是是基於另一種解決方案修改爲a predicate posted by @joel76用於將列表拆分爲兩個等長列表。我所做的修改使得謂詞可以通過包含0或1個元素的「中間」列表作爲參數而獲得奇數長度的列表。它還使用same_length限制參數以避免某些參數的終止問題。我包含了一個簡單的實現same_length/2,並非所有的Prolog都有(它包含在SWI Prolog中)。

swap(L, S) :- 
    same_length(L, S), 
    div(L, Front, Middle, Back),  % L is divided into two halfs & middle 
    append(Back, Middle, NewFront), % S is Back + Middle + Front 
    append(NewFront, Front, S). 

% List L consists of Left + Middle + Right where Left and Right are equal length 
% and Middle has maximum length of 1 
% 
div(L, Left, Middle, Right) :- 
    split(L, L, Left, Middle, Right). 

split(L, [], [], [], L). 
split([H|T], [_], [], [H], T). 
split([H|T], [_, _|T1], [H|T2], M, Right) :- 
    split(T, T1, T2, M, Right). 

% same_length/2 is pre-defined in SWI Prolog and succeeds if the arguments 
% are lists of equal length 
% 
same_length([], []). 
same_length([_|Xs], [_|Ys]) :- same_length(Xs, Ys). 
+0

而且,爲什麼不採用你的原版呢?這裏'split/3'會有一些困難 – false