Prolog: calculate index for a list of list - prolog

I'm new to prolog. I want to implement a predict called high/3. This predict supports two scenarios: One is to return the index of one character in a list of list, for example:
high([[a,b,c],[d,e,f], [g,h]], b, X) returns X = 1
high([[a,b,c],[d,e,f], [g,h]], f, X) returns X = 2
The second scenario is if you provide an index, it should also return all characters at that index position.
e.g.
high([[a,b,c],[d,e,f], [g,h]], X, 1) returns X = b; X= e; X= h
high([[a,b,c],[d,e,f], [g,h]], X, 2) returns X = c; X= f.
I wrote the following predirect:
high([[X|_]|L], X, 0).
high([[Head|Tail]|L], X, H):- member(X, Tail), high([Tail|L], X, H1),H is H1 + 1.
high([[Head|Tail]|L], X, H):- not(member(X, Tail)), high(L, X, H).
This predict only works for the first scenario, but it doesn't work properly for the second scenario.
If I run high([[a,b,c],[d,e,f], [g,h]], X, 1), it only returns X = b, but I expect it returns b, e, h there one by one.
Why does it return only b and fail?

It's a bit unclear what it should do in cases where there are identical elements in different lists. Nevertheless, here's my solution using library(clpfd):
:- use_module(library(clpfd)).
high([H|_], X, I) :-
high_(H, X, I, 0).
high([_|T], X, I) :-
high(T, X, I).
high_([X|_], X, I, I).
high_([_|T], X, I, J) :-
J #>= 0,
J #=< I,
J1 #= J + 1,
high_(T, X, I, J1).
This has the following behaviour:
?- high([[a,b,c],[d,e,f],[g,h]], b, I).
I = 1 ;
false.
?- high([[a,b,c],[d,e,f],[g,h]], f, I).
I = 2 ;
false.
?- high([[a,b,c],[d,e,f],[g,h]], X, 1).
X = b ;
X = e ;
X = h ;
false.
?- high([[a,b,c],[d,e,f],[g,h]], X, 2).
X = c ;
X = f ;
false.
But also works when there are duplicates:
?- high([[a,a],[b,a]], a, X).
X = 0 ;
X = 1 ;
X = 1 ;
false.
With unknown sublists:
?- high([A,B], X, 2).
A = [_4552, _4558, X|_4566] ;
B = [_4552, _4558, X|_4566] ;
false.
With an unknown list of lists:
?- high(L, X, 2).
L = [[_4518, _4524, X|_4532]|_4514] ;
L = [_4512, [_4524, _4530, X|_4538]|_4520] ;
L = [_4512, _4518, [_4530, _4536, X|_4544]|_4526] ;
…

It only returns one result because not(member(X, Tail)) will never be true as long as X hasn't been unified with anything (and Tail is not empty). In other words, since the second clause succeeds, the third one cannot and the recursion does not continue to handle the following lists.
However, I'd say you're going about this the wrong way. Your current code will also give wrong output if an element is present in multiple sublists.
You can break down your problem into smaller parts: you need to be able to relate and element to its index inside a single, simple list; and you need to be able to evaluate this for all sublists in your total list.
First things first: relate and element to its index:
index([X|_], X, 0).
index([_|T], X, I) :- index(T, X, I2), I is I2 + 1.
Very simple and easy to understand, right?
Now to recurse through all lists and match all elements/indices in them:
high([H|_], X, I) :- index(H, X, I).
high([_|T], X, I) :- high(T, X, I).
This will give all the expected outputs:
?- high([[a,b,c],[d,e,f], [g,h]], b, X)
X = 1;
false.
?- high([[a,b,c],[d,e,f], [g,h]], f, X)
X = 2;
false.
high([[a,b,c],[d,e,f], [g,h]], X, 1).
X = b;
X = e;
X = h;
false.
high([[a,b,c],[d,e,f], [g,h]], X, 2).
X = c;
X = f;
false.

Related

Incrementing value on backtrack

how can I do increment on backtracking ... so that goal(S) receives incremented number .. every time it fails on the next run I want to get the next number
S1 is S + 1,goal(S1)
does not work, because :
?- S=0, S1 is S+1.
S = 0,
S1 = 1.
?- S=0,between(1,3,_), S1 is S+1.
S = 0,
S1 = 1 ;
S = 0,
S1 = 1 ;
S = 0,
S1 = 1.
this work
%%counting
baz(..,C) :- .... arg(...), Y is X + 1, nb_setarg(...), goal(Y), ...
foo(..C) :- ....baz(....,C)..., foo(...C).
%%counter
blah :- ....foo(....,counter(0))...
this is not working, i think cause the recursive foo() would force baz() to initialize counter(0)... but i'm good with #sligo solution above
baz(..) :- C = counter(0), .... arg(...), Y is X + 1, nb_setarg(...), goal(Y), ...
foo(..) :- ....baz(....)..., foo(...).
so that goal(S) receives incremented number .. every time it fails on the next run I want to get the next number
That's what between/3 does? Every time on backtracking it makes the next number:
goal(X) :-
write('inside goal, X is '),
write(X),
nl.
test :-
between(0, 3, S),
goal(S).
e.g.
?- test.
inside goal, X is 0
true ;
inside goal, X is 1
true ;
inside goal, X is 2
true ;
inside goal, X is 3
true ;
Edit: From the help for between/3:
between(+Low, +High, ?Value)
Low and High are integers, High >=Low. If Value is an integer,
Low =<Value =<High. When Value is a variable it is successively
bound to all integers between Low and High. If High is inf or
infinite between/3 is true iff Value >=Low, a feature that is
particularly interesting for generating integers from a certain value.
(And see the comments on the help page by LogicalCaptain)
Use non-backtrackable destructive assignment predicate nb_setarg/3:
?- C = counter(0), between(1, 3, _), arg(1, C, X), Y is X + 1, nb_setarg(1, C, Y).
C = counter(1),
X = 0,
Y = 1 ;
C = counter(2),
X = 1,
Y = 2 ;
C = counter(3),
X = 2,
Y = 3.
Alternatives:
foo(C) :-
between(1, inf, C),
goal(C),
!.
baz(C) :-
C = counter(0),
repeat,
arg(1, C, X),
Y is X + 1,
nb_setarg(1, C, Y),
goal(Y),
!.
goal(X) :-
X > 9.
Examples:
?- foo(C).
C = 10.
?- baz(C).
C = counter(10).

Given a few facts in prolog. I can't seem to create a rule which returns the variables don't have a relationship with given variable

The facts are listed below. I can't seem to create the proper rule called notneighbors which returns all the letters that are not neighbor to the given letter. Or don't have a neighborletters(X,Y) relationship with given letter.
neighborletters(a,b,beggining_letters).
neighborletters(b,a,beggining_letters).
neighborletters(j,k,middle_letters).
neighborletters(k,j,middle_letters).
neighborletters(x,y,last_letters).
neighborletters(y,x,last_letters).
So far I have come up with the below rule and many other variations of it.
notneighbor(X,Y):-
neighborletters(A,B, _),
neighborletters(B,A, _),
X \= A,
Y \= B.
but whenever I do a query with the above rule.
?- notneighbor(a, X).
I get a boolean false instead of the desired returns
j
k
x
y
In your query, notneighbor(a, X), the second argument is not instantiated. Therefore, the last goal in your rule, Y \= B, is false as a variable can always be unified with any term. Try instead:
neighbor_letters(a, b, beggining_letters).
neighbor_letters(b, a, beggining_letters).
neighbor_letters(j, k, middle_letters).
neighbor_letters(k, j, middle_letters).
neighbor_letters(x, y, last_letters).
neighbor_letters(y, x, last_letters).
not_neighbor(X, Y):-
neighbor_letters(X, _, _),
neighbor_letters(Y, _, _),
X \== Y,
\+ neighbor_letters(Y, X, _),
\+ neighbor_letters(X, Y, _).
This will give you:
| ?- not_neighbor(a, X).
X = j ? ;
X = k ? ;
X = x ? ;
X = y
yes
This definition for the not_neighbor/2 predicate can also return pairs of letters that are not neighbors:
| ?- not_neighbor(X, Y).
X = a
Y = j ? ;
X = a
Y = k ? ;
X = a
Y = x ? ;
X = a
Y = y ? ;
...
Still an issue with it, however, it returns duplicated answers. E.g.
| ?- not_neighbor(b, y).
yes
| ?- not_neighbor(y, b).
yes
Is that a problem? If yes, can you solve it continuing from here?

Rule to test whether two lists contain same two elements fails due to uniqueness constraint

I'm trying to create a rule called redundancy that examines lists to see if two elements appear together in more than one list.
Here is my code:
columns([a,b,c]).
columns([b,c,d]).
in(X, [H|_]) :-
X = H.
in(X, [_|T]) :-
in(X, T).
redundancy(X, Y) :-
columns(A),
columns(B),
A \= B,
X \= Y,
in(X, A),
in(X, B),
in(Y, A),
in(Y, B).
The problem is the constraint X \= Y. I want it in there to exclude instances where X and Y are identical elements, which would be true for all single elements that appear in more than one list. But it only returns false for the given columns even though it should return permutations of b and c.
?- redundancy(U, T).
false.
If I comment out the constraint I get the expected elements along with the unwanted ones mentioned above.
?- redundancy(X, Y).
X = Y, Y = b ;
X = b,
Y = c ;
X = c,
Y = b ;
X = Y, Y = c ;
X = Y, Y = b ;
X = b,
Y = c ;
X = c,
Y = b ;
X = Y, Y = c ;
false.
Is there a way to enforce this constraint? I'm also interested in ideas to restrict results to a given combination of elements rather than permutations.
Simply move X \= Y to the last line of your predicate. also, see prolog-dif and instantiation-error.
The thing to avoid is using non-pure predicates with not-yet-instantiated logical variables (unless this is exactly what you intended, and you know what you're doing).
Another thing to notice is that X \= Y is not a constraint (that's dif), but a check.

Fibonacci in Prolog - Breaks if False

I have a program fib(X,Y). If Y is the Xth Fibonacci number it returns True else it should return False. My program breaks anytime I input statement which is false.
fib(R,V) :- fib(0,1,R,V).
fib(X, Y, 0, V) :- Y == V.
fib(X, Y, R, V) :- Z is X + Y, C is R - 1, fib(Y, Z, C, V).
fib(0,1) -> True
fib(1,1) -> True
fib(2,2) -> True
fib(3,3) -> True
fib(4,5) -> True
fib(3,5) -> Won't finish.
What do I do wrong? I am using https://swish.swi-prolog.org/ to run my program queries.
The problem here is that you write two clauses fib(X, Y, 0, V) :- and fib(X, Y, R, V) :-. Prolog uses backtracking: in case one clause has been tried, it wil - regardless of sucess or failure - also later retry the next clause (there are some meta-predicates like once/1 that can alter this).
So even if R is 0, or lower, Prolog will also try the second clause.
A quick way to fix this is by using a guards for the second clause:
fib(_, Y, 0, V) :-
Y == V.
fib(X, Y, R, V) :-
R > 0,
Z is X + Y,
C is R - 1,
fib(Y, Z, C, V).
Furthermore your code is not very elegant in the sense that you can not use the relation in a reversed way, nor can we query for the X-th element.
For instance you use Y == V, but this blocks unification: if we want to know the X-th fibonacci number, we want a way to propagate the result back. So we can use unification instead:
fib(_, V, 0, V).
fib(X, Y, R, V) :-
R > 0,
Z is X + Y,
C is R - 1,
fib(Y, Z, C, V).
But now we still do not have a bidirectional relation: we can not obtain the X for a given value V. This is more complex. The easiest way is probably using clpfd for this:
:- use_module(library(clpfd)).
fib(_, V, 0, V).
fib(X, Y, R, V) :-
R #> 0,
V #>= Y,
Z is X + Y,
C #= R - 1,
fib(Y, Z, C, V).
Now we can:
enumerate all indices and the corresponding Fibonacci numbers:
?- fib(A,B).
A = 0,
B = 1 ;
A = B, B = 2 ;
A = B, B = 3 ;
A = 4,
B = 5 ;
A = 5,
B = 8 ;
A = 6,
B = 13 ;
A = 7,
B = 21
...
Obtain the i-th Fibonacci number:
?- fib(2,B).
B = 2 ;
false.
?- fib(10,B).
B = 89 ;
false.
obtain the i for which the corresponding Fibonacci number is a certain value:
?- fib(A,1).
A = 0 ;
A = 1 ;
false.
?- fib(A,2).
A = 2 ;
false.
?- fib(A,3).
A = 3 ;
false.
?- fib(A,4).
false.
?- fib(A,5).
A = 4 ;
false.
Check if the i-th Fibonacci number is a given value:
?- fib(4,5).
true ;
false.
?- fib(4,6).
false.
?- fib(4,10).
false.
?- fib(5,8).
true ;
false.

swi prolog unified return elements in list

I want to return all elements in a list like the result below in X
?return_list_members([1,2,3,4,5], X).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
X = 5.
I have the following code but it also returns the empty list element [] witch is not desirable.
return_member(X, X).
return_list_members([], []).
return_list_members([H|T], X) :- return_member(H, X); return_list_members(T, X).
output when questioned
?return_list_members([1,2,3,4,5], X).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
X = 5 ;
X = [].
also the true or false at the end values are not desirable at the end.
The goal is to achieve a function witch outputs like the built-in function between/3 to be used in a foreach statement
Note that the procedure you are trying to write is the builtin predicate member/2.
?- member(X, [1,2,3,4,5]).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
X = 5.
You can also write your own definition, e.g.:
return_list_members([X|_], X).
return_list_members([_|T], X):-
return_list_members(T, X).
and if you don't want the interpreter to return 'false' at the end, you can add another clause at the beginning (as the first clause):
return_list_members([X], X):- !.
Note however that this clause will have side effects if you call this procedure with the first parameter uninstantiated.
I tried to write between_/3:
between_(X, X, X) :-
!.
between_(X, Y, X) :-
X < Y.
between_(X, Y, N) :-
X < Y,
T is X + 1,
between_(T, Y, N).
The first clause it's required to avoid the final false (as already noticed by gusbro).

Resources