2016-04-21 13 views
0

Ich arbeite an einem Prolog-Algorithmus, der einen "Swap" auf einer Liste durchführen wird.Prolog Type Fehler

Beispiel:

Input: [1,2,3,4] -> Output: [3,4,1,2] Input: [1,2,3,4,5] -> Ausgabe: [4,5,3,1,2]

Die erste Hälfte und die zweite Hälfte der Liste tauschen die Plätze, wenn es eine ungerade Zahl gibt, behält das mittlere Element seine Position bei. Ich habe mit einem Algorithmus kommen, aber ich erhalte eine Fehlermeldung:

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

Mein Code ist wie folgt:

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) 
    ). 

Das Problem ist, dass in ‚Mischen‘, wenn ich trim (L nennen, Runde (Länge/2), A) der Typ ist nicht ganzzahlig? Ich verstehe, dass Länge/2 keine Ganzzahl ist (am wahrscheinlichsten ein Gleitkomma) und ich dachte, rund wäre äquivalent zu Ganzzahl (Ausdruck), die rundet und den Datentyp in eine ganze Zahl transformiert. Ich habe auch versucht, runde mit dem truncate (Ausdruck) und Integer (Ausdruck) zu ersetzen, aber ich erhielt die gleichen Fehler. Kann jemand erklären, was ich falsch mache?

Antwort

1

round ist keine Funktion, es ist ein Prädikat. Ich habe nicht auf den Rest des Codes gesucht, aber diese Linie sollte

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

EDIT sein: BTW, du bist es Grübeln.

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

+1

Ihre vorgeschlagene Implementierung von 'swap/2' macht nicht das, was das OP will. – lurker

+0

Ich bin neu in Prolog im Allgemeinen, also nicht super klar auf all die Wortspiele. Danke für die Klärung des Typproblems. Ich bin immer noch in einer imperativen Denkweise der Programmierung gefangen. Es macht Sinn, dass ich ein Prädikat verwenden müsste, um den Wert zu erhalten und dann wieder einstecken. Auch danke für den Code-Vorschlag. Können Sie die letzte Zeile erklären? –

+0

@lurker: Oh, in der Tat nicht! Stellt sich heraus, ich kann nicht lesen. – Amadan

2

Prolog macht keine Inline-Ausdrucksauswertung. So funktionieren Anrufe wie trim(L2, Length/2 , B) und trim(L, round(Length/2), A) nicht wie erwartet. Ausdrücke werden nur dann spezifisch ausgewertet, wenn bestimmte Operatoren wie is/2, arithmetische Vergleiche oder ihre CLP (FD) -Antworten verwendet werden. Diese Ausdrücke müssten folgendermaßen ausgeführt werden: L is Length // 2, trim(L2, L, B) und R is round(Length/2), trim(L, R, A), wenn dies wörtlich gemacht wird.

Ihre Lösung könnte jedoch wie folgt zusammengefasst werden.

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

Könnten Sie die Details wie das funktioniert? Spezifische Fragen: in "length (Left, M) woher kam Left?" Im if/else-Block gehe ich davon aus, dass "N/\ 1 =: = 1" ob gerade oder ungerade ist In den Append-Statements bin ich wieder unsicher, wo links und rechts herkommen, aber was macht das "[H | Right] "do/stand für? –

+1

@BrianSizemore' Left "ist eine Variable, die ich erfunden habe. Wenn Sie' length (Left, M) 'nennen, dann findet Prolog Lösungen für diese Abfrage, die eine beliebige Liste namens' Length' ist Wenn 'M' bekannt ist, dann erstellt Prolog eine Liste mit unbestückten Elementen und erstellt so eine leere Liste der Länge' M' (siehe Handbuch für arithmetische Operatoren, also ja, 'N/\ 1' und ist ein 1 Bit mit der Nummer. Ich hätte 'mod' verwenden können. Die Schreibweise' [H | T] 'für Listen repräsentiert eine Liste mit dem ersten Element (Kopf)' H' und dem Rest der Liste (Schwanz) 'T'.'=: =' vergleicht Ergebnisse von Ausdruckauswertungen. – lurker

+1

@BrianSizemore Ich habe dem Code einige Kommentare hinzugefügt und das '/ \' für ein wenig mehr Klarheit in einen 'Mod' geändert. Im Prolog-Interpreter können Sie mit 'append/3' spielen und sehen, was es macht, wenn Sie Variablen anstelle von instanziierten Listen haben. Probieren Sie zum Beispiel 'append (A, B, [a, b, c])' und sehen Sie, was es tut. Versuchen Sie auch, 'Länge (L, 5)', um zu sehen, was das tut. – lurker

0

eine idiomatische Lösung:

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. 

es nicht um eine echte Beziehung ist, da es, wenn im Modus swap(-,+) genannt hängt, aber es verhält sich besser nach der Klammerung uncommenting, und die Bereitstellung dieser Schnipsel:

:- 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). 

bearbeiten

nach @false 'Kommentare, ich versuche, die Motivation dieser Antwort zu erklären. Die angefragte swap/2 'Funktion' scheint ein idealer Kandidat zu sein, um die Besonderheit des Prolog-Datenmodells zu demonstrieren, obwohl die geforderte Erklärung (über die korrekte syntaktische Verwendung von Integer-Arithmetik für Listen) hier nicht einmal angedeutet wird.

Von Anfang an von Prolog haben wir eine einzigartige Mischung von relational und funktionale (rekursive) Tools, die für einen Anfänger wenig Sinn machen könnte. Zumindest überrascht es mich immer noch ... und ich mag diese Tatsache.

Jetzt versucht die funktionale Logikprogrammierung, zum Beispiel Curry, eine Lösung durch "Verengung". Von der verknüpften Seite:

Nun, wenn_/1 ist es ein einfacher Ansatz für das gleiche Problem. Auf dem Boden liegend wurde der Swap/2 als eine Funktion beschrieben, aber könnte als eine Beziehung implementiert werden?

@false Vorschlag, Hinzufügen same_length(L,S) als erstes Ziel, behebt den Swap-Modus (-, +), aber Schleifen auf Swap (-, -). Der auf when_/1 basierende Ansatz meldet stattdessen die Unfähigkeit, die Konjunktion zu erden.

bearbeiten

Termination Fragen auseinander, meine Wahl der Ziele um ist wirklich unwirksam. Hinting diese Antwort auf another question, kam mir der Gedanke, dass ein großer Effizienzgewinn (zumindest für den Modus swap (+, -)) kann die erste Stelle setzt Einschränkungen erhalten:

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

Wann wäre/2 eine "wahre" Relation? Der Fall, den Sie erwähnen, wird leicht durch Hinzufügen von 'same_length (L, S)' als erstes Ziel behoben. Wäre es dann durch eine wahre Beziehung? – false

+0

@false: zu schwierig, ich habe nur einige Alternativen angedeutet, die OP nach den einleitenden lurker 'Kommentaren schätzen könnte. Natürlich ist Ihr Fix viel besser, als wenn Sie sich über die gesamte Konjunktion verteilen. Denken Sie, ich sollte diese Antwort löschen, für den Fall, dass es nur verwirrend sein könnte? Danke – CapelliC

+1

Es ist OK als Antwort. Trotzdem möchte ich wissen, was du mit wahrer Beziehung meinst. Nach dem Fix wird 'swap (L, [a | L])' immer noch nicht beendet, obwohl es keine Lösung gibt (= endlich viele Lösungen). – false

0

Hier ist eine andere Lösung, die auf eine Basis Änderung an a predicate posted by @joel76 zum Aufteilen einer Liste in zwei gleich lange Listen. Die von mir vorgenommene Änderung ermöglicht, dass das Prädikat mit einer Liste ungerader Länge erfolgreich ist, indem die "mittlere" Liste von 0 oder 1 Elementen als Argument einbezogen wird. Es verwendet auch same_length, um die Argumente einzuschränken, um ein Abbruchproblem für bestimmte Argumente zu vermeiden. Ich habe eine einfache Implementierung von same_length/2 eingefügt, die nicht alle Prologs haben (es ist in SWI Prolog enthalten).

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

Und warum nicht auch deine Originalversion übernehmen? Dies wird einige Schwierigkeiten mit "Split/3" haben – false