Prolog: zip 2 lists into one - prolog

Suppose, we have 2 lists [1, 2, 3], [3, 2, 1] and we want to create a third list equal to the result of a binary function applied to every corresponding pair of the lists elements.
Something like:
f(x, y) =
-1, if x < y;
0, if x = y;
1 if x > y.
For our example we get [-1, 0, 1].
I created the code:
f(A, B, C) :- ( A < B ->
C = -1
; A =:= B ->
C = 0
; C = 1
).
zip([A], [B], [C]) :- f(A, B, C).
zip([A | T1], [B | T2], [C | T]) :- f(A, B, C), zip(T1, T2, T).
It works:
?- zip([1,2,3], [3,2,1], X).
X = [-1, 0, 1] .
But I have a feeling that I am reinventing the wheel. Is there a system function for this in Prolog?

You probably want to use a maplist predicate
https://www.swi-prolog.org/pldoc/doc_for?object=maplist/4
just like so
?- maplist(f,[1,2,3],[3,2,1],C).
C = [-1, 0, 1].

Related

Prolog unification doesn't evaluate arithmetic expression

Suppose, I wanted to write a program in prolog, which accepts a number input X, and outputs all value pairs for which the sum is X.
some_pred(X,X1,X2) :-
X1 + X2 = X.
This does not work, because X1 + X2 is not evaluated arithmetically.
some_pred(X,X1,X2) :-
Xtemp is X1 + X2,
Xtemp = X.
The other option I have also doesn't work, because X1 and X2 are not instantiated.
How would someone solve this?
Yes, unification doesn't evaluate arithmetic expressions, and if it did that wouldn't help you because X1 and X2 are undefined so adding them together is meaningless.
You need either to write a search yourself such as a brute force nested loop:
sum_a_b(X, A, B) :-
between(1, X, A),
between(1, X, B),
X is A + B.
Or a more nuanced one where you encode something about arithmetic into it, start with 1+(X-1) and then (2+X-2), etc:
sum_a_b(X, A, B) :-
between(0, X, A),
B is X - A.
Or more generally, learn about clpfd (link1, link2) which can do arithmetic evaluating and solving for missing variables in equations, as well as searching through finite domains of possible values:
:- use_module(library(clpfd)).
sum_a_b(X, A, B) :-
[A, B] ins 1..X,
X #= A + B.
? sum_a_b(5, A, B), label([A, B]).
A = 1,
B = 4 ;
A = 2,
B = 3 ;
...
NB. I'm assuming positive integers, otherwise with negatives and decimals you'll get infinite pairs which sum to any given X.
Here's something very similar, using a list:
pos_ints_sum(Sum, L) :-
compare(C, Sum, 1),
pos_ints_sum_(C, L, Sum).
% 0 means the list has ended
pos_ints_sum_(<, [], 0).
% 1 means there is only 1 possible choice
pos_ints_sum_(=, [1], 1).
pos_ints_sum_(>, [I|T], Sum) :-
% Choose a number within the range
between(1, Sum, I),
% Loop with the remainder
Sum0 is Sum - I,
pos_ints_sum(Sum0, T).
Result in swi-prolog:
?- pos_ints_sum(5, L).
L = [1, 1, 1, 1, 1] ;
L = [1, 1, 1, 2] ;
L = [1, 1, 2, 1] ;
L = [1, 1, 3] ;
L = [1, 2, 1, 1] ;
L = [1, 2, 2] ;
L = [1, 3, 1] ;
L = [1, 4] ;
L = [2, 1, 1, 1] ;
L = [2, 1, 2] ;
L = [2, 2, 1] ;
L = [2, 3] ;
L = [3, 1, 1] ;
L = [3, 2] ;
L = [4, 1] ;
L = [5].
Note: X is a poor choice of variable name, when e.g. Sum can easily be used instead, which has far more meaning.

Turn List into number, increment the number, and then turn the number into a list

I have my head stuck in this exercise in prolog, I ve been trying to do it on my own but it just won't work. Example: ?-succesor([1,9,9],X) -> X = [2,0,0]. Had tried first to reverse the list and increment it with 1 and then do a if %10 = 0 the next element should be incremented too. Thing is that I m too used with programming syntax and I can't get my head wrapped around this.Any help would be appreciated.
I have done this so far, but the output is false.
%[1,9,9] -> 199 +1 -> 200;
numbers_atoms([],[]).
numbers_atoms([X|Y],[C|K]) :-
atom_number(C, X),
numbers_atoms(Y,K).
%([1,2,3],X)
digits_number(Digits, Number) :-
numbers_atoms(Digits, Atoms),
number_codes(Number, Atoms).
number_tolist( 0, [] ).
number_tolist(N,[A|As]) :-
N1 is floor(N/10),
A is N mod 10,
number_tolist(N1, As).
addOne([X],[Y]):-
digits_number(X,Y1), %[1,9,9] -> 199
Y1 is Y1+1, % 199 -> 200
number_tolist(Y1,[Y]), % 200 -> [2,0,0]
!.
You can solve this problem similarly to how you would solve it manually: traverse the list of digits until you reach the rightmost digit; increment that digit and compute the carry-on digit, which must be recursively propagated to the left. At the end, prepend the carry-on digit if it is equal to 1 (otherwise, ignore it).
% successor(+Input, -Output)
successor([X0|Xs], L) :-
successor(Xs, X0, C, Ys),
( C = 1 % carry-on
-> L = [C|Ys]
; L = Ys ).
% helper predicate
successor([], X, C, [Y]) :-
Z is X + 1,
Y is Z mod 10,
C is Z div 10. % carry-on
successor([X1|Xs], X0, C, [Y|Ys]) :-
successor(Xs, X1, C0, Ys),
Z is X0 + C0,
Y is Z mod 10,
C is Z div 10. % carry-on
Examples:
?- successor([1,9,9], A).
A = [2, 0, 0].
?- successor([2,7],A), successor(A,B), successor(B,C), successor(C,D).
A = [2, 8],
B = [2, 9],
C = [3, 0],
D = [3, 1].
?- successor([7,9,9,8], A), successor(A, B).
A = [7, 9, 9, 9],
B = [8, 0, 0, 0].
?- successor([9,9,9,9], A), successor(A, B).
A = [1, 0, 0, 0, 0],
B = [1, 0, 0, 0, 1].
Here's a version which doesn't use is and can work both ways:
successor(ListIn, ListOut) :-
reverse(ListIn, ListInRev),
ripple_inc(ListInRev, ListOutRev),
reverse(ListOutRev, ListOut).
ripple_inc([], [1]).
ripple_inc([0|T], [1|T]).
ripple_inc([1|T], [2|T]).
ripple_inc([2|T], [3|T]).
ripple_inc([3|T], [4|T]).
ripple_inc([4|T], [5|T]).
ripple_inc([5|T], [6|T]).
ripple_inc([6|T], [7|T]).
ripple_inc([7|T], [8|T]).
ripple_inc([8|T], [9|T]).
ripple_inc([9|T], [0|Tnext]) :-
ripple_inc(T, Tnext).
e.g.
?- successor([1,9,9], X).
X = [2, 0, 0]
?- successor([1,9,9], [2,0,0]).
true
?- successor(X, [2,0,0]).
X = [1, 9, 9]
although it's nicely deterministic when run 'forwards', it's annoying that if run 'backwards' it finds an answer, then leaves a choicepoint and then infinite loops if that choicepoint is retried. I think what causes that is starting from the higher number then reverse(ListIn, ListInRev) has nothing to work on and starts generating longer and longer lists both filled with empty variables and never ends.
I can constrain the input and output to be same_length/2 but I can't think of a way to constrain them to be the same length or ListOut is one item longer ([9,9,9] -> [1,0,0,0]).
This answer tries to improve the previous answer by #TessellatingHacker, like so:
successor(ListIn, ListOut) :-
no_longer_than(ListIn, ListOut), % weaker than same_length/2
reverse(ListIn, ListInRev),
ripple_inc(ListInRev, ListOutRev),
reverse(ListOutRev, ListOut).
The definition of no_longer_than/2 follows. Note the similarity to same_length/2:
no_longer_than([],_). % same_length([],[]).
no_longer_than([_|Xs],[_|Ys]) :- % same_length([_|Xs],[_|Ys]) :-
no_longer_than(Xs,Ys). % same_length(Xs,Ys).
The following sample queries still succeed deterministically, as they did before:
?- successor([1,9,9], X).
X = [2,0,0].
?- successor([1,9,9], [2,0,0]).
true.
The "run backwards" use of successor/2 now also terminates universally:
?- successor(X, [2,0,0]).
X = [1,9,9]
; false.

find biggest interval in a list of sublists (these sublists have 2 elements)

i have this exercise where i get a list of sublists like [[X1,Y1],[X2,Y2]...] which represent an interval (Xi-Yi), and i want to return a list with the biggest interval (it can be more than one interval).
this is what i've got so far.
i can't see what im doing wrong but when try to run biggest_interval([[1,2],[5,7],[6,10],[12,15]],L). i get true, followed by a false instead of [6,10]
biggest_interval([H|T],Answer):-
biggest_interval(H,T,-1,Answer).
biggest_interval([],_,_,_).
biggest_interval(_,[],_,_).
biggest_interval([X,Y],[H|T],Biggest,Answer):-
Z is Y-X,
Z =:= Biggest,
append(Answer,[X,Y],L),
!,
biggest_interval(H,T,Biggest,L).
biggest_interval([X,Y],[H|T],Biggest,Answer):-
Z is Y-X,
(
Z > Biggest -> append([],[X,Y],L),
biggest_interval(H,T,Z,L);
true
),
biggest_interval(H,T,Biggest,Answer).
One of the problems with your code is that your predicate biggest_interval/4 does not collect the Answer in the "base case" (it only stops the recursive process).
One possible solution is:
biggest_interval(ListOfLists, Answer) :-
biggest_interval(ListOfLists, -inf, [], Biggest),
reverse(Biggest, Answer). % if order of the pairs is important!
biggest_interval([], _, Answer, Answer) :- !. % collect Answer!
biggest_interval([[X,Y]|Lists], Max, Acc, Answer) :-
Z is Y-X,
( Z = Max -> biggest_interval(Lists, Max, [[X,Y]|Acc], Answer)
; Z > Max -> biggest_interval(Lists, Z, [[X,Y]], Answer)
; biggest_interval(Lists, Max, Acc, Answer) ).
Here are some examples:
?- biggest_interval([[1,2],[5,7],[6,10],[12,15]],L).
L = [[6, 10]].
?- biggest_interval([[1,20],[5,7],[6,10],[12,15]],L).
L = [[1, 20]].
?- biggest_interval([[1,2],[5,7],[6,10],[12,15],[3,10]],L).
L = [[3, 10]].
?- biggest_interval([[1,2],[5,7],[6,10],[12,15],[8,12]],L).
L = [[6, 10], [8, 12]].
Here is another way to do it, with functionnal design :
:- use_module(library(lambda)).
biggest_interval([[H1, H2]|T], Out) :-
D1 is H2 - H1,
foldl(\X^Y^Z^(X = [A,B],
D is B - A,
Y = [Delta, L],
( Delta > D
-> Z = Y
; ( Delta = D
-> append(L, [X], NL),
Z = [Delta, NL]
; Z = [D, [X]]))), T, [D1, [[H1,H2]]], [_, Out]).
example :
?- biggest_interval([[1,2],[5,7],[6,10],[12,15]],L).
L = [[6, 10]].
?- biggest_interval([[1,2],[5,7],[6,10],[12,15],[8,12]],L).
L = [[6, 10], [8, 12]].

Create a list from zero down to a negative number

I want to make a program in which the user will give a negative number and it will return a list starting from zero till that number. Here is a desired output example
create(-5,L).
L = [0,-1,-2,-3,-4,-5]
could you help me in any way, please?
I would break it up into two auxiliary predicates. The auxiliary predicate is helpful for building the list in the direction you desire.
create(N, L) :-
N < 0,
create_neg(N, 0, L).
create(N, L) :-
N >= 0,
create_pos(N, 0, L).
create_neg(N, N, [N]).
create_neg(N, A, [A|T]) :-
A > N,
A1 is A - 1,
create_neg(N, A1, T).
create_pos(N, N, [N]).
create_pos(N, A, [A|T]) :-
A < N,
A1 is A + 1,
create_pos(N, A1, T).
This will put them in the right order as well:
| ?- create(-5, L).
L = [0,-1,-2,-3,-4,-5] ? a
no
| ?- create(5, L).
L = [0,1,2,3,4,5] ? a
no
| ?-
What you're after is not really a program, just an 'idiomatic' pattern:
?- findall(X, (between(0,5,T), X is -T), L).
L = [0, -1, -2, -3, -4, -5].
Note the parenthesis around the Goal. It's a compound one...
Another way:
?- numlist(-5,0,T), reverse(T,L).
...
Since you provided your code (which as mentioned in comments would be better to appear in your question), one problem I think is that with X>0 and X<0 clauses-cases you will have infinite recursion, maybe it would be better to use abs/1:
create(0,[0]).
create(X,[X|T]):- Y is abs(X), Y > 0,
(X>0 -> N is X-1 ; N is X+1),
create(N,T).
Though still one problem:
?- create(-5,L).
L = [-5, -4, -3, -2, -1, 0] ;
false.
?- create(5,L).
L = [5, 4, 3, 2, 1, 0] ;
false.
The list is built reversed so you could reverse it at the end like:
create_list(N,L):- create(N,L1), reverse(L1, L).
And now:
?- create_list(5,L).
L = [0, 1, 2, 3, 4, 5] ;
false.
?- create_list(-5,L).
L = [0, -1, -2, -3, -4, -5] ;
false.

Prolog - merge digits to number

I want to merge list of digits to number.
[1,2,3] -> 123
My predicate:
merge([X], X).
merge([H|T], X) :-
merge(T, X1),
X is X1 + H * 10.
But now I get:
[1,2,3] -> 33
Another way to do it would be to multiply what you've handled so far by ten, but you need an accumulator value.
merge(Digits, Result) :- merge(Digits, 0, Result).
merge([X|Xs], Prefix, Result) :-
Prefix1 is Prefix * 10 + X,
merge(Xs, Prefix1, Result).
merge([], Result, Result).
The math is off. You're rule says you have to multiply H by 10. But really H needs to be multiplied by a power of 10 equivalent to its position in the list. That would be * 100 for the 1, and * 10 for the 2. What you get now is: 10*1 + 10*2 + 3 which is 33. The problem is that your recursive clause doesn't know what numeric "place" the digit is in.
If you structure the code differently, and use an accumulator, you can simplify the problem. Also, by using CLP(FD) and applying some constraints on the digits, you can have a more general solution.
:- use_module(library(clpfd)).
digits_number(Digits, X) :-
digits_number(Digits, 0, X).
digits_number([], S, S).
digits_number([D|Ds], S, X) :-
D in 0..9,
S1 #= S*10 + D,
digits_number(Ds, S1, X).
?- digits_number([1,2,3], X).
X = 123
?- digits_number(L, 123).
L = [1, 2, 3] ;
L = [0, 1, 2, 3] ;
L = [0, 0, 1, 2, 3] ;
L = [0, 0, 0, 1, 2, 3] ;
L = [0, 0, 0, 0, 1, 2, 3]
...
?-

Resources