Prolog Type Error

2019-03-03 13:02发布

问题:

I'm working on a prolog algorithm that will perform a "swap" on a list.

Example:

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

The first half and second half of the list swap places, if there is an odd number then the center element retains it's position. I have come up with an algorithm, but I am getting an error:

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

My code is as follows:

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

The problem is that in 'mixing' when I call trim (L, round(Length/2), A) the type is not integer? I understand that Length/2 is not an integer (most likely a float) and I thought round was equivalent to integer(expr) which rounds and transforms the data type to an integer. I also tried replacing round with the truncate(expr) and integer(expr), but I was receiving the same errors. Can someone explain what I'm doing wrong?

回答1:

round is not a function, it's a predicate. I haven't looked at the rest of the code, but that line should be

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

EDIT: BTW, you're overthinking it.

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



回答2:

Prolog doesn't do inline expression evaluation. Thus, calls such as trim(L2, Length/2 , B) and trim(L, round(Length/2), A) will not work as you expect. Expressions are only evaluated in specific when using certain operators such as is/2, arithmetic comparisons, or their CLP(FD) counterparts. These expressions would need to be done as: L is Length // 2, trim(L2, L, B) and R is round(Length/2), trim(L, R, A) if done literally.

Your solution could be condensed, however, as follows.

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


回答3:

Here's another solution that is based upon a modification to a predicate posted by @joel76 for splitting a list into two equal length lists. The modification I made enables the predicate to succeed with an odd-length list by including the "middle" list of 0 or 1 elements as an argument. It also uses same_length to constrain the arguments to avoid a termination issue for certain arguments. I included a simple implementation of same_length/2 which not all Prologs have (it is included with 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).


回答4:

an idiomatic solution:

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.

it's not a true relation, since it hangs if called in mode swap(-,+), but it behaves better after uncommenting the bracketing, and providing this snippet:

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

edit

after @false' comments, I'm trying to explain the motivation of this answer. The requested swap/2 'function' seems to be an ideal candidate to showcase the peculiarity of Prolog data model, albeit the requested explanation (about proper syntactic usage of integer arithmetic applied to lists) is not even hinted here.

From the very start of Prolog we have available an unique mixing of relational and functional (recursive) tools, that could make little sense to a newbie. At least, it still surprises me... and I like this fact.

Now, functional logic programming, for instance Curry, attempts a solution through 'narrowing'. From the linked page:

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

Now, when_/1 it's a simple minded approach to the same problem. Down to earth, the swap/2 has been described as a function, but could be implemented as a relation ?

@false suggestion, adding same_length(L,S) as first goal, fixes the swap(-,+) mode, but loops on swap(-,-). The approach based on when_/1 instead reports the inability to ground the conjunction.

edit

Termination issues apart, my choice of goals order is really ineffective. Hinting this answer to another question, occurred to me that a big efficiency gain (at least, for the mode swap(+,-)) can be obtained putting constraints first:

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, 3210999 Lips)


标签: types prolog