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?
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
).
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).
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).
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)
Related
i want to sort my list in ascending order but without using builtin function is that possible?
5 9 3 4 7 10 2 1 5 6
if yes then how?
You can implement the sorting algorithm yourself, writing predicates. Let’s implement it using selection sort.
The first thing to write is a min predicate which relates a list to its minimal element and remaining sublist:
% In this clause, the list has a single element.
min([H], H, []).
% In this clause, the head is not the minimal element of the list.
min([H|L], M, [H|R]) :- min(L, M, R), H >= M.
% In this clause, the head is the minimal element of the list.
min([H|L], H, [M|R]) :- min(L, M, R), H < M.
Once you have this predicate, it is easy to write a sorted predicate which relates a list to the list sorted in ascending order:
% In this clause, the list is empty.
sorted([], []).
% In this clause, the sorted list in ascending order is the concatenation of
% its minimal element and the sorted remaining sublist in ascending order.
sorted(L, [M|S]) :- min(L, M, R), sorted(R, S).
This sorting algorithm has a time complexity of O(n2) which is bad, but it is easy to understand and implement. You definitely would prefer to implement a better sorting algorithm, such as merge sort which has a time complexity of O(n log n). The goal here is just to show how this can be done with Prolog.
We can just express our wishes, and Prolog will take it as its command:
ascending( [], [] ).
ascending( [A], [A] ).
ascending( A, [X,Y|C] ) :-
select( X, A, B),
ascending( B, [Y|C] ),
X < Y.
This runs very slowly though:
2 ?- ascending( [5,9,3,4,7,10,2,1,5,6], X).
false.
% Execution Aborted
3 ?- ascending( [5,9,3,4,7,10,2,1,6], X).
X = [1, 2, 3, 4, 5, 6, 7, 9, 10] ;
false.
listelements([i|o],listelements) :-
u is abs((listmax)-(listmin)),
listelements(u,listelements).
listmin([min],min).
listmin([x|y],z) :-
listmin(y,h),
min(x,h,z).
min(Q,W,Q) :-
Q =< W.
min(Q,W,W) :-
W < Q.
listmax([max],max).
listmax([a|b],c) :-
listmax(b,f),
max(a,f,c).
max(M,N,M) :-
M =< N.
max(M,N,N) :-
N < M.
Arithmetic: `listmax' is not a function
This is the error. Help please!
When I ask ?- listmin([1,2,3,4,5,6],z).
it answers false instead of 1. Why?
Considering your concrete choice of arithmetic predicates,
I guess you want to support all kinds of Prolog numbers (floating point numbers, integers, and rational numbers).
If you only work with integer numbers here then make sure you are using clpfd!
In this answer we proceed step-by-step, like this:
We define an auxiliary predicate num_num_absdiff/3 which computes the absolute difference of its first two arguments.
num_num_absdiff(X,Y,D) :-
D is abs(X-Y).
To apply num_num_absdiff/3 to all adjacent items and gather the results, we use mapadj/3:
?- mapadj(num_num_absdiff,[1,2,4,8,16,100],Ds).
Ds = [1, 2, 4, 8, 84].
The maximum absolute difference is the maximum number in above list.
We employ list_maxnum/2 to get said number.
?- list_maxnum([1,2,4,8,84], Max).
Max = 84.
Let's put it all together and define maxabsdiff_of/2:
maxabsdiff_of(D,Nums) :-
mapadj(num_num_absdiff,Nums,Ds),
list_maxnum(Ds,D).
Here are some sample queries:
?- maxabsdiff_of(D,[1,2,3,4,5,6]). % as given by the OP
D = 1.
?- maxabsdiff_of(D,[1,2,3,4,5,6,8]).
D = 2.
?- maxabsdiff_of(D,[1,2,3,100,4,5,6,8]).
D = 97.
Of course, you could also use lambda together with meta-predicate foldl/4, like so:
:- use_module(library(lambda)).
maxabsdiff_of(X,[V0,V1|Vs]) :-
foldl(\E^(E0+D0)^(E+D)^(D is max(D0,abs(E-E0))), Vs, V1+abs(V1-V0),_+X).
Sample queries:
?- maxabsdiff_of(D,[1,2,3,4,5,6]).
D = 1.
?- maxabsdiff_of(D,[1,2,3,4,5,6,8]).
D = 2.
?- maxabsdiff_of(D,[1,2,3,100,4,5,6,8]).
D = 97.
Same results as before!
I am working on a longer problem that has me duplicate an element N times in list form, and I believe that using append is the right way to go for this. The tiny predicate should theoretically act like this:
?- repl(x,5,L).
L = [x, x, x, x, x] ;
false.
I cannot seem to find any tips for this online, the replication of a single element, but I believe we need to use append, but no recursive solution. I come from more of a Haskell background, where this problem would be much easier to perform. Can someone help get me started on this? :)
Mine so far:
repl(E, N, R) :-
N > 0, append([E], [], R), writeln(R), repl(E, N-1, R), fail.
Which gives me:
?- repl(x,5,L).
[x]
[x]
[x]
[x]
[x]
false.
Close but not quite!
A recursive approach would be straight-forward and would work. I recommend figuring that one out. But here's a fun alternative:
repl(X, N, L) :-
length(L, N),
maplist(=(X), L).
If N is instantiated, then length(L, N) will generate a list of length N of just "blanks" (don't care terms). Then maplist(=(X), L) will unify each element of L with the variable X.
This gives a nice, relational approach and yields sensible results in the general case:
| ?- repl(X, N, L).
L = []
N = 0 ? ;
L = [X]
N = 1 ? ;
L = [X,X]
N = 2 ? ;
| ?- repl(X, N, [x,x,x]).
N = 3
X = x
yes
...
To figure out a recursive case, think about what your base case looks like (it would be repl with a count of 0 - what does the list look like then?). In the recursive case, think in terms of:
repl(X, N, [X|T]) :- ...
Meaning: The list [X|T] is the element X repeated N times if.... Figure out if what? If your base case is length 0, then your recursion is probably going to describe the repl of a list of length N in terms of the repl of a list of length N-1. Don't forget in this recursive rule to ensure N > 0 to avoid infinite recursion on backtracking. If you don't need the predicate to be purely relational and assume N is instantiated, then it can be fairly simple.
If you make a simple recursive version, you can "wrap" it in this predicate to make it work with variable N:
repl(X, N, L) :-
length(L, N),
simple_recursive_repl(X, N, L).
...
Because length/2 is relational, it is much more useful than just providing the length o a given list. When N and L are not instantiated, it becomes a generator of variable lists, starting at length 0. Type, length(L, N). at the Prolog prompt and see what happens.
Determinism
You give the following example of the predicate you envision:
?- repl(x,5,L).
L = [x, x, x, x, x] ;
false.
Notice that the ; is not very productive here. If you want to repeat x 5 times, then this can be done in exactly one way. I would therefore specify this predicate as deterministic not nondeterministic as you are doing.
Repeating list
Your code is actually quite far off a working solution, despite the output looking quite close in spirit to the envisioned result. You try to define the base case and the recursive case at the same time, which will not work.
Here is a simple (but less fun than #lurker gave :-)) implementation of the base and recursive case:
repeating_list(_, 0, []):- !.
repeating_list(H, Reps1, [H|T]):-
Reps2 is Reps1 - 1,
repeating_list(H, Reps2, T).
In a sense #lurker's implementation is simpler, and it is surely shorter.
Some extensions
In real-world/production code you would like to catch type errors and treat different instantiations with the same predicate. The second clause checks whether a given list consists of repeating elements (and if so, which one and how many occurrences there are).
%! repeating_list(+Term:term, +Repeats:integer, -List:list(term)) is det.
%! repeating_list(?Term:term, ?Repeats:integer, +List:list(term)) is det.
repeating_list(_, 0, []):- !.
% The term and number of repetitions are known given the list.
repeating_list(H, Reps, L):-
nonvar(L), !,
L = [H|T],
forall(
member(X, T),
% ==/2, since `[a,X]` does not contain 2 repetitions of `a`.
X == H
),
length([H|T], Reps).
% Repetitions is given, then we generate the list.
repeating_list(H, Reps1, [H|T]):-
must_be(nonneg, Reps1), !,
Reps2 is Reps1 - 1,
repeating_list(H, Reps2, T).
% Repetitions is not `nonneg`.
repeating_list(_, Reps, _):-
domain_error(nonneg, Reps).
Notice that I throw a domain error in case the number of repetitions is negative. This uses library error in SWI-Prolog. If your Prolog does not support this feature, then you may leave the last clause out.
PS: Comparison to Haskell
The combination of your statement that you do not know how to solve this problem in Prolog and your statement that this problem can be solved much easier in Haskell seems a little strange to me. I think you can only compare the difficulty of two implementations once you know how both of them look like.
I do prefer findall/3 to build lists, and between/3 to work with ranges:
repl(E, N, L) :- findall(E, between(1, N, _), L).
I have to create list of n elements for example,
do_list(5,L1).
should return,
L1=[1,2,3,4,5].
This is what I have, but it's not working.
do_list(X,L1):- X1 is X-1, do_list(X1,[X1|L1]).
do_list(0,[]).
If you want to create a list of consecutive numbers from 1 to N you can use builtin predicates findall/3 and between/3 this way:
do_list(N, L):-
findall(Num, between(1, N, Num), L).
?- do_list(5,L).
L = [1, 2, 3, 4, 5].
SWI also has another builtin which does just that, numlist/3:
?- numlist(1,5,L).
L = [1, 2, 3, 4, 5].
There are three problems with your code. The first problem is that you add X1 to the list in the clause body, but you never pass the new list back towards the head of the clause. I.e., L1 is an accumulator variable, but you need a third argument that will be bound to the final list.
The second is that the second clause only matches if the input list is empty. This will never be the case, since you add X1 to the list before calling do_list/2 recursively. I.e., you don't have a recursion anchor, and the goal ?- do_list(5,L) will never return.
The third problem is that you add X1 to the list instead of X. You'd skip the largest number.
This is how it should work:
do_list(N, L) :- do_list1(N, [], L).
do_list1(0, L, L) :- !.
do_list1(N, R, L) :- N > 0, N1 is N-1, do_list1(N1, [N|R], L).
Or if you don't want to use any built-in function (like me, when I tried this as a practice and then run into this question), you can use this (working but not effective) solution:
connect([],X,X).
connect([H|T],C,[H|T2]) :- connect(T,C,T2).
revert([],[]).
revert([H|T],R) :- revert(T,Trev), connect(Trev,[H],R)
do_revlist(0,[]).
do_revlist(X,[X|L]) :- X1 is X-1, do_revlist(X1,L).
do_list(X,L2) :- do_revlist(X,L), revert(L,L2).
P.S. Works only for positive integers.
Another solution similar to that of twinterer but without cut or predefined predicates, employing an accumulator.
do_List(Max,L) :- do_ListAcc(1,Max,L). % call Accumulator
do_ListAcc(N,N,[N]). % N=:=N ends recursion
do_ListAcc(Min,Max,[Min|Succs]) :-
Next is Min + 1,
do_ListAcc(Next,Max,Succs).
This also works only for positive integers.
I've been trying to teach myself Prolog for a few weeks. Right now I'm trying to find all ways to make a large integer from several smaller integers, using a predicate partition/3 that I want to work like:
| ?- partition(4, [1, 2, 3], X).
X = [1, 1, 1, 1] ? ;
X = [1, 1, 2] ? ;
X = [1, 3] ? ;
X = [2, 2] ? ;
no
thus finding all ways to make 4 from 1, 2 and 3. Duplicate solutions like [1, 2, 1] and [2, 1, 1] are fine but probably not hard to avoid. Here's what I have right now:
partition(N, _, []) :- N = 0.
partition(N, [], _) :- fail.
partition(N, [IH|IT], [OH|OT]) :-
N =< 0, fail;
N > IH, M is N-IH, OH = IH,
partition(M, [IH|IT], OT).
% if the first input IH can be subtracted from N,
% do so into M and push IH into the output list [OH|OT]
partition(N, [_|IT], Output) :-
N =< 0, fail;
partition(N, IT, Output).
% after trying the first input term, try the others
The idea is that N will eventually become zero, and the subtractions that got it there will be placed in the third argument as a list. The third and fourth rules only operate on positive integers, the second rule says not to run out of inputs, and the first rule signals that the partition is valid when N reaches zero. Problem is, I only get:
| ?- partition(4, [1, 2, 3], X).
no
The first and second rules make sense to me, the third and fourth seem iffy but I can't find anything specifically wrong with them. I thought that the output tail OT might not get instantiated when M becomes zero, but the first rule takes care of that. Or is there some fundamental misunderstanding of how Prolog works (which seems likely to happen often for me)?
Also, are the N =< 0, fail; parts redundant? They seem redundant but I can't be sure until I get something that works.
Edit: I'm using GNU Prolog.
You have one issue here, with the comparison of N to IH:
partition(N, [IH|IT], [OH|OT]) :-
N =< 0, fail;
N > IH, [...]
It should be N >= IH.
Consider your example partition(4, [1, 2, 3], X):
It matches predicate 3, which in turn checks partition(3,[1,2,3],OT)
partition(3,[1,2,3],OT) matches the third predicate, that checks partition(2,[1,2,3],OT).
partition(2,[1,2,3],OT) matches the third predicate, that checks partition(1,[1,2,3],OT).
partition(1,[1,2,3],OT) matches the fourth predicate, since 1 > 1 fails. Checks partition(1,[2,3],OT).
partition(1,[2,3],OT) will match the fourth predicate to the end, and fail when the list is empty.