Related
How do I implement in Prolog the predicate list_for_set(Xs, Cs) where Cs is a list that contains the same elements as Xs, in the order of its first occurrence, but whose number of occurrences is only 1. For example, the query
? - list_for_set([1, a, 3.3, a, 1.4], Cs).
it happens only for Cs = [1, a, 3,4]. The consultation
? - list_for_set ([1, a, 3,3, a, 1,4], [a, 1,3,4])
must fail.
The Cs list of the previous statement will be called a set list, that is, a list with only one occurrence of each element.
Ok, there is some trickery involved.
foofilter([],_,_-T) :- T=[]. % close difflist
foofilter([L|Ls],Seen,H-T) :-
member(L,Seen),
!,
foofilter(Ls,Seen,H-T).
foofilter([L|Ls],Seen,H-T) :-
\+member(L,Seen),
!,
T=[L|NewT],
foofilter(Ls,[L|Seen],H-NewT).
:-begin_tests(filter).
data([1, a, 3, 3, a, 1, 4]).
test(one) :- data(L),
DiffList=[[]|T]-T, % Assume [] is never in L
foofilter(L,[],DiffList),
DiffList=[_|Result]-_,
format("~q ==> ~q\n",[L,Result]),
Result = [1,a,3,4].
:-end_tests(filter).
rt :- run_tests(filter).
Run tests:
?- rt.
% PL-Unit: filter [1,a,3,3,a,1,4] ==> [1,a,3,4]
. done
% test passed
true.
Someone will probably come up with a one-liner.
I would like to ask, if anyone knows how to improve (if it's not optimal) this code.
The idea, is that you have a list of elements, and I want to return a list, with two sublists inside it, the first sublist should contain the elements that are contained in the odd positions of the list, and the second sublist should contain, the elements that are contained in the even positions of the list.
Some examples:
?-evenAndOdd([1,2,3,4,5],[[1,3,5],[2,4]])
True.
?-evenAndOdd([a,b,c,d,e],[[a,c,e],[b,d]]).
True.
The code I have implemented is the next one:
evenAndOdd([],[]).
evenAndOdd([H|R],NL):-
evenAndOddRec([H|R], [[],[]],1,NL).
evenAndOddRec([], [LOdd,LEven],_,[LOdd,LEven]).
evenAndOddRec([H|R],[LOdd,LEven],Pos,NL):-
\+ even(Pos),
!,
NPos is Pos +1,
append(LOdd,[H],NLOdd),
evenAndOddRec(R,[NLOdd,LEven],NPos,NL).
evenAndOddRec([H|R],[LOdd,LEven],Pos,NL):-
NPos is Pos + 1,
append(LEven, [H], NLEven),
evenAndOddRec(R,[LOdd, NLEven],NPos,NL).
even(N):-
N mod 2 =:=0.
One symptom that the code is not optimal is that it will run off into the woods if you ask for an additional solution in the -,+,+ instantiation pattern:
?- evenAndOdd(X, [[1,3,5], [2,4,6]]).
X = [1, 2, 3, 4, 5, 6] ;
<time passes>
This kind of thing is a frequent occurrence when manually trying to match up lists with indexes in Prolog.
Stylistically, I would rather not give back a list containing exactly two lists when I could just have three arguments instead of two; this is, after all, a relationship between three lists, the combined list and the even and odd items.
Additionally, just eyeballing it, I'm not sure why any arithmetic or any cuts are needed here. This is how I would implement it:
evenAndOdd([], [], []).
evenAndOdd([O], [O], []).
evenAndOdd([O,E|Rest], [O|ORest], [E|ERest]) :- evenAndOdd(Rest, ORest, ERest).
This works with many instantiations:
?- evenAndOdd([1,2,3,4,5,6], O, E).
O = [1, 3, 5],
E = [2, 4, 6].
?- evenAndOdd([1,2,3,4,5], O, E).
O = [1, 3, 5],
E = [2, 4] ;
false.
?- evenAndOdd(X, [1,3,5], [2,4]).
X = [1, 2, 3, 4, 5] ;
false.
?- evenAndOdd(X, [1,3,5], [2,4,6]).
X = [1, 2, 3, 4, 5, 6].
?- evenAndOdd(X, [1,3,5], [2,4,6,8]).
false.
?- evenAndOdd([1,2,3,4,5,6], X, [2,4,6,8]).
false.
?- evenAndOdd([1,2,3,4,5,6], X, [2,4,6]).
X = [1, 3, 5].
You can implicitly determine even and odd values upon recursion, by taking two elements at a time (and taking into account when the has an odd number of elements):
evenAndOdd(L, [LOdd, LEven]):-
evenAndOdd(L, LOdd, LEven).
evenAndOdd([], [], []).
evenAndOdd([Odd], [Odd], []).
evenAndOdd([Odd,Even|Tail], [Odd|LOdd], [Even|LEven]):-
evenAndOdd(Tail, LOdd, LEven).
% appends an element to the beginning of a list.
append_element(X, T, [X|T]).
% append a list to another list to create a combined list,
% by breaking the first list apart, and using append_element.
append_list([], L, L).
append_list([H|T], L, NewList) :-
append_element(H, L, NL),
append_list(T, NL, NL).
When I try to run append_list,
?- append_list([1,2], [3, 4, 5], NL).
I get back false. Instead of
NL = [2, 1, 3, 4, 5].
Why?
I have a predict which gets first N elements:
nfirst(N, _, Lnew) :- N =< 0, Lnew = [].
nfirst(_, [], []).
nfirst(N, [X|Y], [X|Y1]) :- N1 is N - 1, nfirst(N1, Y, Y1).
It works:
% nfirst(3,[1,2,3,4,5,6],X).
% X = [1, 2, 3]
I need a predict for divide list like below:
% divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
% X = [[a,b,c],[d,e],[f],[g,h]]
The best way is using nfirst.
Very similar question to the one I answered here. Again, the trick is to use append/3 plus length/2 to "bite off" a chunk of list, per my comment above:
split_at(N, List, [H|[T]]) :- append(H, T, List), length(H, N).
If you run that, you'll see this:
?- split_at(4, [1,2,3,4,5,6,7,8], X).
X = [[1, 2, 3, 4], [5, 6, 7, 8]] ;
So this is the backbone of your program, and now you just need the usual recursive stuff around it. First, the base case, which says, if I'm out of list, I should be out of split locations, and thus out of result:
divide([], [], []).
Note that explicit base cases like this make your program more correct than something like divide([], _, _) because they will cause you to fail if you get too many split locations for your list size.
Now the recursive case is not difficult, but because split_at/3 puts two things together in a list (probably a bad choice, you could make split_at/4 as an improvement) you have to take them out, and it clouds the logic a bit here while making (IMO) a nicer API on its own.
divide(List, [Split|Splits], [Chunk|Rest]) :-
split_at(Split, List, [Chunk, Remainder]),
divide(Remainder, Splits, Rest).
This should be fairly straightforward: we're just taking a Split location, using it to chop up the List, and repeating the processing on what's left over. It seems to work as you expect:
?- divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
X = [[a, b, c], [d, e], [f], [g, h]] ;
false.
Hope this helps! Compare to the other answer, it may illuminate things.
How can I write a relation in prolog that determines if there are any two pairs in a list with the same sum. The relation should fail if there exist no pairs whose sums are equal. The relation should also fail if the list contains less than four elements.
list([1 2 3]) fails since it only has 3 elements
list([2 3 4 1]) succeeds since 2+3=4+1
list([3 1 2 4 5 6]) succeds since 5+1=2+4
list([1 8 20 100]) fails since there are no pairs with equal sums
How about this algorithm: take any two pairs of numbers, and see if they match. Here is the code for it:
has_equal_sums(List) :-
select(A, List, List2),
select(B, List2, List3),
select(C, List3, List4),
select(D, List4, _),
A+B =:= C+D.
If you want to make sure it works, or for debug purpose, you can display the two selected pairs as an output:
has_equal_sums(List, [[A, B], [C, D]]) :-
select(A, List, List2),
select(B, List2, List3),
select(C, List3, List4),
select(D, List4, _),
A+B =:= C+D.
Here are a few examples of usage:
?- has_equal_sums([1, 2, 3, 6, 5], X).
X = [[1,6],[2,5]] ? ;
X = [[2,6],[3,5]] ?
?- has_equal_sums([1, 2, 3, 5], X).
no
?- has_equal_sums([1, 2, 3], X).
no
So I checked with my professor and since our deadline has passed, he is OK with me posting my solution to this problem. This is probably not the most succinct way to solve the problem, and I'm leaning on my Scheme a bit, but it appears to work:
%car operations
car([],null).
car([X|_],X).
cadr([_|L],R) :-
car(L,R).
caddr([_|L],R) :-
cadr(L,R).
%cdr operations
cdr([],[]).
cdr([_|L],L).
cddr([_|L],R) :-
cdr(L,R).
cdddr([_|L],R) :-
cddr(L,R).
%two-pair operation
% This algorithm is based on the provided example
% solution for CSC388FA09HW4.
long-enough(L,_) :-
length(L,X),
X>3.
too-long(L,_) :-
length(L,X),
X>4.
two-pair([Head|Tail]) :-
long-enough([Head|Tail],_),
(
(car(Tail,N2),cadr(Tail,N3),caddr(Tail,N4),Head+N2=:=N3+N4);
(cadr(Tail,N2),car(Tail,N3),caddr(Tail,N4),Head+N2=:=N3+N4);
(caddr(Tail,N2),car(Tail,N3),cadr(Tail,N4),Head+N2=:=N3+N4)
);
too-long([Head|Tail],_),
(
two-pair(Tail);
cdr(Tail,N2),two-pair([Head|N2]);
car(Tail,N2),cddr(Tail,N3),two-pair([Head|[N2|N3]]);
car(Tail,N2),cadr(Tail,N3),cdddr(Tail,N4),two-pair([Head|[N2|[N3|N4]]])).