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?
round
is not a function, it's a predicate. I haven't looked at the rest of the code, but that line should beEDIT: BTW, you're overthinking it.an idiomatic solution:
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: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:
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:
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 ofsame_length/2
which not all Prologs have (it is included with SWI Prolog).Prolog doesn't do inline expression evaluation. Thus, calls such as
trim(L2, Length/2 , B)
andtrim(L, round(Length/2), A)
will not work as you expect. Expressions are only evaluated in specific when using certain operators such asis/2
, arithmetic comparisons, or their CLP(FD) counterparts. These expressions would need to be done as:L is Length // 2, trim(L2, L, B)
andR is round(Length/2), trim(L, R, A)
if done literally.Your solution could be condensed, however, as follows.