Building a list of n length same value Prolog - list

I want to build/3 a list in Prolog of N elements, each element should be X.
?- build(30,3,L).
L = [30,30,30].
Spent a good few hours on it, keep ending up in either a infinite loop or the variables aren't instantiated properly.
build(_,0,[]).
build(X,N1,[X]):- N1>0, N1 is N - 1, build(X,N,[]).
build(X,N1,[X|L]) :- N1 > 0, N1 is N - 1, build(X,N,L).

Using meta-predicates makes it very short:
(1) with maplist/2: creates a list of length N and then matches all its elements to X.
build(X, N, List) :-
length(List, N),
maplist(=(X), List).
(2) with findall/3: makes loop N-times and completes List with X N-times
build(X, N, List) :-
findall(X, between(1, N, _), List).

build(_,0,[]). % any value, repeated 0 times, makes for an empty list
good.
build(X,N1,[X|L]) :- % a value X, repeated N1 times, makes for [X|L] list, _if_ ...
N1 > 0, N1 is N - 1, % N1 is positive, and L is
build(X,N,L). % one element shorter... right?
excellent. huh? N is N1 - 1 you mean.
build(X,N1,[X]):- N1>0, N1 is N - 1, build(X,N,[]).
why?? [X] is already matched by the previous rule, [X] = [X | [] ] = [X | L], and the empty list L = [] will be matched by the first rule.
You don't need this rule at all.

Related

Delete an element at a specified index, return untouched list if given negative or 0 as an index

Hi I really am needing help with this Prolog problem. I want to delete an element at a specified index but only when given an index that is greater than 0. If the index is 0 or less, I would like to just return the list untouched.
For example:
delete(List,Index,NewList).
?-L=[1,2,3,4],delete(L,2,L2),write(L2).
L2 = [1,3,4]
for <= 0:
delete(List,Index,NewList).
?-L=[1,2,3,4],delete(L,-1,L2),write(L2).
L2 = [1,2,3,4]
I have managed to handle the first case using the following code.
remove([_|T], 1, T).
remove([H|T1], N, [H|T2]):-
N > 1,
I is N - 1,
remove(T1, I, T2).
I attempted using prologs if-else equivalent syntax but was not able to get it working.
EDIT: Thank you so much for the responses! I did not realize I was missing another case.
You just need to include one more base case in the predicate definition:
remove(L, N, L) :-
N =< 0.
remove([_|T], 1, T).
remove([H|T1], N, [H|T2]):-
N > 1,
I is N - 1,
remove(T1, I, T2).
Examples:
?- L = [one, two, three, four], remove(L, 2, R).
L = [one, two, three, four],
R = [one, three, four] ;
false.
?- L = [one, two, three, four], remove(L, -1, R).
L = R, R = [one, two, three, four] ;
false.
You just need another rule for handling N =< 0:
remove(L, N, L) :-
N =< 0.
remove([_|T], 1, T).
remove([H|T1], N, [H|T2]):-
N > 1,
I is N - 1,
remove(T1, I, T2).

How to get prime numbers from list and put them in empty list

I want to get all prime numbers from a list of numbers and put it into another empty list.
My problem is that whenever the function isPrime is false, the program is terminated.
I'm very beginner in prolog, so if you have any feedback I'll appreciate the help.
Here is my code below:
check_prime(X):-
Xtemp is integer(X/2),
isPrime(X,Xtemp).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
isPrime(_,2).
isPrime(2,_).
isPrime(Num,Counter):-
X is Counter-1,
X \= 0,
X2 is mod(Num,X),
X2 \= 0,
isPrime(Num,X).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
prime_list([],Y).
prime_list([H|T],[H|T2]):-
check_prime(H),
prime_list(T,T2).
Your check_prime function will give true even for non-prime numbers.
Example: check_prime(4) will call isPrime(4, 2), which will unify with the first clause of isPrime.
An example of code that gives you the list of primes would be this:
% predicate to check if X has any divisors
divisible(X,Y) :- 0 is X mod Y, !.
divisible(X,Y) :- X > Y+1, divisible(X, Y+1).
%predicate to check if that number is prime by using the divisible predicate
isPrime(2) :- true,!.
isPrime(X) :- X < 2,!,false.
isPrime(X) :- not(divisible(X, 2)).
%predicate that returns the resulted list
primeList([], []). % stopping condition, when list empty
% we add current element to the resulting list if it is prime
primeList([H|T], [H|R]):- isPrime(H), !, primeList(T, R).
% otherwise, we just skip it
primeList([_|T], R):- primeList(T, R).
Query: ?-primeList([1,2,3,4,5,6,7,8,9], R). => R=[2,3,5,7]

Give as a solution every different number in a list of lists

I need to do a predicate, select(ListOfLists, X) that returns as a solution every different number in a list of lists, starting with the numbers that are alone in a list, for example:
select([[1,2,3],[1,2],[4],[3]],X).
Would return:
X = 4 ;
X = 3 ;
X = 2 ;
X = 1
Order doesn't matter as long as the numbers that are alone in the list are shown first.
To do this, first I coded 2 other predicates, which are:
%OrderedList is Lists ordered by size.
orderListsBySize(Lists, OrderedLists).
Example: orderListsBySize([[1,2],[6],[3,4,5]], L). ->L = [[6], [1,2], [3,4,5]]
And
%ListsWithoutX is Lists without the X elements
removeFromLists(X, Lists, ListsWithoutX).
Example: removeFromLists(1,[[1,2],[3],[4,1,5]],L). -> L = [[2],[3],[4,5]]
Both predicates work.
Then, to do the select(ListOfLists, X) predicate, I tried the following:
select([[X|[]]|_], X). select(L1,S) :-
orderListsBySize(L1, [[X|XS]|LS]),
length(XS, A),
A == 0,
select([[X|[]]|M], S),
removeFromLists(X, [XS|LS], M).
select([[X|_]|_], X).
But it doesn't work.
It's not a hard exercise to do in other languages, the problem is that it's still hard for me to understand how prolog works. I appreaciate any help, thanks!
You could start with:
select2(ListOfLists,Element):-
length(List,_Len),
member(List,ListOfLists),
member(Element,List).
Which will return all the answers, but then get stuck in a loop looking for ever bigger lists.
This can be averted using the :-use_module(library(clpfd)). and defining a fd_length/2 which wont keep looking for bigger lists then exist in the list of lists.
fd_length(L, N) :-
N #>= 0,
fd_length(L, N, 0).
fd_length([], N, N0) :-
N #= N0.
fd_length([_|L], N, N0) :-
N1 is N0+1,
N #>= N1,
fd_length(L, N, N1).
select(ListOfLists,Element):-
maplist(length,ListOfLists,Lengths),
sort(Lengths,SortedLength),
last(SortedLength,Biggest),
Biggest #>= Len,
fd_length(List,Len),
member(List,ListOfLists),
member(Element,List).
Example Query:
?-select([[1,2,3],[1,2],[4],[3]],X).
X = 4
X = 3
X = 1
X = 2
X = 1
X = 2
X = 3
false
If you want unique solutions, you could enclose in a setof/3 and then call member/2 again.

Prolog - average predicate: Arguments not sufficiently instantiated

I have a list of cars (auto in german), where the first Variable is the license-plate and the second one the speed:
[auto(eu-ts884, 69), auto(dn-gh184, 64), auto(ac-lj123, 72)].
Now I try to write an average predicate but it fails with the error message:
ERROR: Arguments are not sufficiently instantiated
My code so far:
durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
Y is S/L,
L > 0,
cardinal([auto(_, X)|Tail], L),
sumKilometer([auto(_, X)|Tail], S).
sumKilometer([], 0).
sumKilometer([auto(_, X)|Tail], Sum) :-
sumKilometer(Tail, N),
Sum is N + X.
cardinal([], 0).
cardinal([_|Tail], Result) :-
cardinal(Tail, N),
Result is N + 1.
My code is quite equivalent to that post, although I cannot make out my mistake.
Note: sumKilometer and cardinal are working fine.
You write:
durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
Y is S/L,
L > 0,
cardinal([auto(_, X)|Tail], L),
sumKilometer([auto(_, X)|Tail], S).
The first problem is that when you call durchschnitt([auto(foo,2)],L,Y), L is a free variable. As a result, you cannot calculate Y is S/L since both S and L are unknown here.
You can however use:
durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
cardinal([auto(_, X)|Tail], L),
sumKilometer([auto(_, X)|Tail], S),
Y is S/L.
So here you calculate the average after both L and S are known. Furthermore you do not unify the list with [auto(_,X)|Tail], etc. A simple check like A = [_|_] is sufficient:
durchschnitt([], 0, 0).
durchschnitt(A, L, Y):-
A = [_|_],
cardinal(A, L),
sumKilometer(A, S),
Y is S/L.
This will also reduce the amount of time spent packing and unpacking.
Sum, Length and Average all concurrently
You can construct a predicate that calculates the three all at the same time (so without looping twice over the list). You can simply use accumulators, like:
durchschnitt(A,L,Y) :-
durchschnitt(A,0,0,L,Y).
Here the second and third element are the running sum and length respectively.
Now for durchschnitt/5, there are two cases. In the first case we have reached the end of the list, and we thus have to calculate the average and return it, like:
durchschnitt([],S,L,L,Y) :-
(L \= 0
-> Y is S/L
; Y = 0).
So we use an if-then-else to check if the length is something different than 0 (in the case there are no autos in the list, we return 0 as average.
In the recursive case, we simple increment the running length and update the running sum, like:
durchschnitt([auto(_,Si)|T],RS,RL,L,Y) :-
RSN is RS+Si,
L1 is L+1,
durchschnitt(T,RSN,L1,L,Y).
Or putting it together:
durchschnitt(A,L,Y) :-
durchschnitt(A,0,0,L,Y).
durchschnitt([],S,L,L,Y) :-
(L \= 0
-> Y is S/L
; Y = 0).
durchschnitt([auto(_,Si)|T],RS,RL,L,Y) :-
RSN is RS+Si,
L1 is L+1,
durchschnitt(T,RSN,L1,L,Y).

Prolog Assignment

This is the question for one of my assignments:
Write repCount(L, X, N) which is true when N is the number of occurrences of X in list L.
Here's my code where I try to tackle the problem recursively:
repCount([], X, N) :-
N is 0.
repCount([H|T], X, N) :-
count([H|T], X, N).
count([], X, 0).
count([H|T], X, N) :-
count(T, X, N1),
X =:= H,
N is N1 + 1.
And it works when I supply a list full of identical numbers like this:
?- repCount([2,2,2], 2, N).
N = 3.
But if I supply a list with at least one different value:
?- repCount([2,2,22], 2, N).
false.
It returns false. I cannot figure out why this happens or how to change it to 'skip' the non-matching value, rather than declare the whole thing false. Any input is appreciated.
count([H|T], X, N):- count(T, X, N1), X=:=H, N is N1 + 1.
here you declare that N should be N1+1 if X is H; however you do not define what should happen if X is not H (basically missing an else clause)
this should work:
count([H|T], X, N):-
count(T, X, N1),
(X=:=H->
N is N1 + 1
; N is N1).
another way would be:
count([H|T], X, N):- count(T, X, N1), X=:=H, N is N1 + 1.
count([H|T], X, N):- X=\=H, count(T, X, N1), N is N1.
but this is inefficient since count(T,X,N1) will be called twice if X is not H. we can fix this by doing the check in the head of the clause:
count([H|T], H, N):- count(T, X, N1), N is N1 + 1.
count([H|T], X, N):- count(T, X, N1), N is N1.
or simply:
count([H|T], H, N):- count(T, X, N1), N is N1 + 1.
count([H|T], X, N1):- X=\=H, count(T, X, N1).
One maybe interesting addition to what #magus wrote: If you only care about the number of elements instead of the elements themselves, you can use findall/3 like this:
list_elem_num(Ls, E, N) :-
findall(., member(E, Ls), Ds),
length(Ds, N).
Preserve logical-purity—with a little help from
meta-predicate tcount/3 and (=)/3!
The goal tcount(=(X),Es,N) reads "there are N items in list Es that are equal to X".
Sample query:
?- tcount(=(X), [a,b,c,a,b,c,a,b,a], N).
( N = 4, X=a
; N = 3, X=b
; N = 2, X=c
; N = 0, dif(X,a), dif(X,b), dif(X,c)
). % terminates universally
But assuming you aren't allowed to 'cheat', if you want to use recursion, you don't need to do the '==' comparison.. you can use Prolog's variable unification to reach the same end:
% Job done all instances
repCount2([], _, 0).
% Head unifies with X/2nd parameter - ie X found
repCount2([H|T], H, N) :-
repCount2(T, H, NewN),
N is NewN + 1.
% We got here, so X not found, recurse around
repCount2([_|T], X, N) :-
repCount2(T, X, N).
In the second predicate, H is mentioned twice, meaning that if the Head of the list is the same as X, then recurse down, then add 1 to the result of the rest of the recursion (which ends in adding 0 - the base case, which is how the accumulator is built).
Almost there...you need to use an accumulator, thus:
repCount(Xs,Y,N) :-
count(Xs,Y,0,N) % the 3rd argument is the accumulator for the count, which we seed as 0
.
count([],_,N,N). % if the list is empty, unify the accumulator with the result
count([X|Xs],Y,T,N) :- % if the list is non-empty,
X == Y , % and the head of the list (X) is the the desired value (Y),
T1 is T+1 , % then increment the count, and
count(Xs,Y,T1,N) % recurse down, passing the incremented accumulator
. %
count([X|Xs],Y,T,N) :- % if the list is non-empty,
X \== Y , % and the head of the list(X) is not the desired value (Y),
count(Xs,Y,T,N) % simply recurse down
. %
The original question didn't say whether there were constraints on which predicates you could use.
If you are allowed to 'cheat' ie. use higher order predicates like 'findall' that recurse for you Vs you doing the recursion yourself, this can be done in a single predicate:
repCount(L, X, N) :-
findall(X, member(X, L), ListOfX),
length(ListOfX, N).