Prolog Finding Kth element in a list - list

I am trying write a predicate in prolog to find Kth element in a list.
Example:
?- element_at(X,[a,b,c,d,e],3).
X = c
my code as follows
k_ele(X,[X|_],1).
k_ele(X,[_|T],Y) :- Y > 1,Y is Y - 1, k_ele(X,T,Y).
But no use, I found solution on Internet as
element_at(X,[X|_],1).
element_at(X,[_|L],K) :- K > 1, K1 is K - 1, element_at(X,L,K1).
Which is same as my logic except they used one extra variable K1.
What is wrong with my code, why I need another variable ?

The reason your code does not work is that unification is not an assignment. When you say
Y is Y - 1
you are trying to unify a value of Y with the value of Y-1, which is mathematically impossible. This is roughly the same as saying 4 is 3 or 1001 is 1000. The entire condition fails, leading to the failure to find the element in the list.
The fixed solution that you have found on the internet introduces a separate variable K1, which is unified with K - 1. This is very much doable: K1 gets the value to which K-1 evaluates, the condition succeeds, and the clause moves on to the recursive invocation part.

Because variables in prolog are write-once critters. Having been [assigned|unified with|bound to] a non-variable value, it ceases to be variable. It is henceforth that value. Unlike more...conventional...programming languages, once bound, the only way to reassign a prolog variable is to backtrack through the assignment and undo it.
It should be noted, though, that a variable can be unified with another variable: Given a predicate something like
foo(X,Y) :- X = Y .
and something like
shazam(X,Y) :- bar(X,Y) , X = 3.
will result in both X and Y being 3. Having been unified, X and Y are both the same variable, albeit with different names.

I imagine you're working with the exercises from this link:
http://www.ic.unicamp.br/~meidanis/courses/problemas-prolog/
Note, in my opinion the original solution is not the best either.
For example, a query:
element_at(X,[a,b,c],Y).
would crash, even if there are 3 solutions:
X = a, Y = 1;
X = b, Y = 2;
X = c, Y = 3;
I believe writing in an alternative way:
element_at(H, [H | _], 1).
element_at(H, [_ | T], N) :- element_at(H, T, NMinus1), N is NMinus1 + 1.
would give better results. It's less efficient as one can not apply the last call optimization, but the logic becomes more general.

Related

Counting occurrences in list

I'm trying to create a rule that counts the number of occurrences of a certain element in a given list, what I've tried so far does not seem to work the way I expect:
The first argument here should be the list, the second one the element we are looking for, and the last one the number of occurrences:
%empty list should always return 0 occurences
count([],E,0) :- true.
%if our head is what we are looking for, count
count([E|T],E,N) :- count(T,E,N-1).
%otherwise, do not count
count([H|T],E,N) :- H \== E, count(T,E,N).
Here H is the head and T the tail of the given list.
The base case e.g. count([],1,N). returns N = 0 as expected, but as soon as the list is non empty, we always get false.:
?- count([1],1,N).
false.
?- count([1,2,1,3],1,N).
false.
Can anyone point out what I'm doing wrong?
Update:
It seems to work when replacing the second line with
count([E|T],E,N+1) :- count(T,E,N).
But I just cannot figure out why this is not equivalent to my first idea.
Then we get
?- count([1,2,1,3],1,N).
N = 0+1+1
which is correct.
Evaluation versus Unification
In Prolog =/2 is a unification operator. It does not evaluate a term as an expression even if the term might represent something numerically evaluable. Similarly, when you use count([E|T], E, N+1), Prolog does not evaluate the term N+1. To Prolog, that's just another term, which internally is represented as +(N, 1).
For a term to be interpreted and evaluated as a numeric expression, you need to use specific Prolog operators that will do that. As #SQB points out, one of these is is/2:
N = 1, N1 is N + 1.
This will result in N1 = 2. However this:
N = 1, N1 = N + 1.
Will result in: N1 = 1 + 1 (or equivalently, N1 = +(1, 1)).
There are also numeric comparison operators in Prolog that will also compute expressions. These are =:=/2, >/2, >=/2, </2 and =</2. So you will see the following:
1 + 2 =:= 3.
Will yield "true" since =:=/2 is specifically for comparing equality of evaluable numeric expressions. However:
1 + 2 = 3.
Will yield "false" because the term +(1,2) does not match (or more accurately, cannot be unified with) the term 3.
Argh! Arguments not sufficiently instantiated!
I've seen quite a few posts on SO regarding an error that their arguments are "not sufficiently instantiated". Many of these are in the use of is/2. As described above, is/2 will evaluate the expression in the 2nd argument and then unify that result with the first argument. The 2nd argument must be fully evaluable (all variables involved in the expression must be instantiated with a numeric value) or you'll get an error. Likewise, when using the expression comparisons, all of the variables in both arguments must be fully instantiated. Thus, if X is a variable that is unbound, the following will yield an "arguments not sufficiently instantiated" error:
9 is X * 3. % Will NOT yield X = 3, but will give an error
Y + 2 =:= X * 2. % Error if either X or Y are not instantiated
Y = 1, X = 2, Y + 2 =:= X * 2. % Yields "false" (fails) since 3 is not equal to 4
Y = 1, X = 2, Y + 2 < X * 2. % Yields "true" (succeeds) since 3 is less than 4
Y = 1, X = 2, X + 1 = Y + 2. % Yields "false" since +(2,1) doesn't unify with +(1,2)
When performing constraint logic on expressions, the tool to use is the CLP(FD) library. Thus:
X * 3 #= 6.
Will yield, X = 2.
The problem is that N+1 (or N-1) isn't evaluated, as can be seen by your second (working) example.
% empty list has 0 occurrences
count([], _, 0).
% if our head is what we are looking for, count
count([E|T], E, N) :-
N_1 is N - 1, % this is the important one
count(T, E, N_1).
% otherwise, do not count
count([H|T], E, N) :-
H \== E,
count(T, E, N).
is actually evaluates the equation, instead of unifying the N in your next call with N-1. That's why in your second example, you end up with N=0+1+1 instead of N=2.

Prolog Recursion removing elements at index that is multiples of n from a list where n is any number

This is my first time asking a question here but I have a problem that I really can't wrap my head around which is Prolog recursion especially when it deals with list. So the task that I am supposed to solve is to write a drop predicate that works like this. For example, drop([1,2,3,4,5,6,7,8,9], 2, L) where L = [1,3,5,7,9] and N=n where elements at position n, 2n, 3n.... will be removed. The list starts from 1 is another thing to be noted.
Here is my attempt so far and thought process:
drop([], _, []).
indexOf([X|_], X, 1). %Using 1 because the question says the first element starts from 1.
indexOf([_|Ys], Y , I):-
indexOf(Ys, Y, N),
I is N + 1.
drop([X|Xs], Y, [X|_]) :-
indexOf([X|Xs] , X , A),
Z is A mod Y,
Z \== 0.
drop([X|Xs], Y, Zs) :-
%indexOf([X|Xs], X, A),
drop(Xs, Y, Zs).
I created an indexOf predicate to find the index of the elements starting from 1 . Next, my idea was to use the my first drop recursive case (in the code above it is the 5th case) to check and see whether the position of the element returns a remainder of zero when divided by the Y (second input). if it does not return a remainder of zero, then the X remains inside the list and is not dropped. Then, prolog moves on to the 2nd drop recursive case which can only be arrived when Z=0 and it will drop X from the list to return Zs. In essence, an element with index n, 2n, 3n... that is returned by indexOf will be dropped if it does not return a remainder of zero when divided by Y (second input).
I have not learnt Cut at this point of the course at the moment. I would appreciate if someone can point me to the right direction. I have been working on this for almost a day.
I am still trying to adapt the logic and declarative thinking in this programming paradigm. I would appreciate it if you could share with me, how did you personally go about mastering Logic programming?
First, looking at your approach, there's a flaw with using the indexOf/3. That is, at a given point in time when you need to know the index of what you're removing, you don't know what the item is yet until you get to it. At that point, the index is 1.
That's one issue with the following rule:
drop([X|Xs], Y, [X|_]) :-
indexOf([X|Xs], X, A),
Z is A mod Y,
Z \== 0.
The first subquery: indexOf([X|Xs], X, A) will succeed with A = 1 on its first attempt, just by definition (of course, X has index 1 in list [X|Xs]. As it succeeds, then the next line Z is A mod Y yields 1 since 1 mod Y is always 1 if Y > 0. And therefore, Z \== 0 will always succeed in this case.
Thus, you get the result: [X|_] where X is the first element of the list. So the first solution you get for, say, drop([1,2,3,4], 2, L). is L = [1|_]. Your third drop/3 predicate clause just recurses to the next element in the list, so then it will succeed the second clause the same way, yielding, L = [2|_], and so on...
Starting from the top, here's a way to think about a problem like this.
Auxiliary predicate
I know I want to remove every N-th element, so it helps to have a counter so that every time it gets to N I will ignore that element. This is done with an auxiliary predicate, drop/4 which will also have a recurring counter in addition to the original N:
drop(L, N, R) :-
drop(L, N, 1, R). % Start counter at 1
Base rule
If I drop any element from the empty list, I get the empty list. It doesn't matter what elements I drop. That's expressed as:
drop([], _, _, []).
You have this rule expressed correctly already. The above is the 4-argument version.
Recursive rule 1 - The N-th element
I have list [X|Xs] and X is the N-th element index, then the result is R if I skip X, reset my index counter to 1, and drop the N-th element from Xs:
drop([_|Xs], N, N, R) :- % I don't care what the element is; I drop it
drop(Xs, N, 1, R).
Recursive rule 2 - Other than the N-th element
I have list [X|Xs] and X is the A-th element (< N), then the result is [X|R] if I increment my index counter (A), and drop N-th elements from Xs with my updated index counter:
drop([X|Xs], N, A, [X|R]) :-
A < N,
NextA is A + 1,
drop(Xs, N, NextA, R).
Those are all the needed rules (4 of them).

Finding Length of List in Prolog

I'm running Prolog and trying to write a small function returning the length of a list:
len([],0).
len([XS], Y) :-
len([X|XS], M),
Y is M+1.
My logic is that the recursive call should include the tail of the list (XS) and increase 1 to the previous length (Y is M+1.)
This always returns false.
Any thoughts?
Here is a general methodology for debugging and testing Prolog predicates:
Start with the most general query!
Think of it: In Prolog you do not need to make up some test data. You don't even need to understand a predicate at all: Just hand in free variables! That is always a professional move!
So in your case, that's
?- len(L,N).
L = [], N = 0
; loops.
Your definition is not that bad as you claim: At least, it is true for the empty list.
Now, maybe look at the compiler warnings you probably received:
Warning: user://1:11:
Singleton variables: [X]
Next read the recursive rule in the direction of the arrow :- that is, right-to-left:
Provided len([X|Xs], M) is true and Y is M+1 is true, provided all that is true, we can conclude that
len([XS], Y) is true as well. So you are always concluding something about a list of length 1 ([Xs]).
You need to reformulate this to len([X|Xs], M) :- len(Xs, N), Y is M+1.
And here is another strategy:
Generalize your program
By removing goals, we can generalize a program1. Here is my favorite way to do it. By adding a predicate (*)/1 like so:
:- op(950,fy, *).
*_.
Now, let's remove all goals from your program:
len([],0).
len([XS], Y) :-
* len([X|XS], M),
* Y is M+1.
What we have now is a generalization. Once again, we will look at the answers of the most general query:
?- len(L, N).
L = [], N = 0
; L = [_].
What? len/2 is only true for lists of length 0 and 1. That means, even len([1,2], N) fails! So now we know for sure: something in the visible remaining part of the program has to be fixed. In fact, [XS] just describes lists of length 1. So this has to be removed...
Fine print:
1 Certain restrictions apply. Essentially, your program has to be a pure, monotonic program.

Easily replicate an element in Prolog :)

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).

Prolog append variable to list

I want to append to a list a variable, N, that's bound to a number.
N = 1.
append([N], [2,3,4], Z).
Z = [N,2,3,4]. //Wrong output!
I want to get Z = [1,2,3,4]
How do I append the number part of a variable, not the actual variable itself?
I'm afraid Prolog doesn't have variable assignment like you're used to, just variable binding. So the "statements"
N = 1.
append([N], [2,3,4], Z).
actually constitute two completely unrelated queries. Fortunately, the effect you desire can be achieved by combining your queries:
N = 1, append([N], [2,3,4], Z).
If you truly need a global variable, you can always use a fact or asserta/1 to define one dynamically.
Also note: in the future, you'll probably want to make sure you use is instead of = when dealing with numbers.
Which Prolog are you using? All Prologs I am aware of, will produce an answer for N = 1 first. This should make it clear, that Prolog has first answered the query N = 1 with N = 1, which might look a little odd at first. E.g., here is GNU:
| ?- N = 1.
N = 1
yes
| ?-
append([N], [2,3,4], Z).
Z = [N,2,3,4]
yes