How to compare lists length Prolog - prolog

I'm new at Prolog and i was trying to solve some exercises and I found this one twice_as_long(L1,L2) that succeeds if the list L2 is twice as long as the list L1.
Do NOT compute the lengths of the lists.
twice_as_long([],[]).
true.
?- twice_as_long([a],[1,2]).
true.
?- twice_as_long([a,b],X).
X = [_G328, _G331, _G334, _G337] ;
false
I want some hint please cuz i don't want to compare lengths like what they said .

We can generalize your second example as a rule:
twice_as_long([_],[_,_]).
But we can do better:
twice_as_long([_|T1], [_,_|T2]) :- twice_as_long(T1,T2).
That, with the base case from you first example, will do the job.

With respect to Prolog conventions, given a predicate of the form type_of_relationship(X,Y), it's pretty customary to read it as a declaration of fact along the lines of
X has a type_of_relationship with Y.
So, let's flip the order of the arguments: twice_as_long(X,Y) asserts that X is twice as long as Y.
Given that, and amplifying the answer from #ScottHunter, Let's break things down into simple cases.
First, your example shows a case that should fail as succeeding.
twice_as_long([],[]).
asserts that the empty list is twice as long as itself, something that is manifestly untrue: the empty list is of length zero and twice zero is still..zero. So that case should be discarded.
There is, however, the simplest case possible:
twice_as_long( [_,_] , [_] ) .
an assertion that a list of length 2 is twice as long as a list of length 1.
Then, there is the generic case:
twice_as_long( [_,_|Xs] , [_|Ys] ) :-
twice_as_long(Xs,Ys) .
in which we say that a list of 2 or more items is twice as long as a list of 1 or more items...
IF, the remainder of the first list is twice as long as the remainder of the second list, where the remainders are obtained by removing the first 2 items from the first list and just 1 item from the second list.
That gives us this as the solution:
twice_as_long( [_] , [_,_] ) .
twice_as_long( [_|Xs] , [_,_|Ys] ) :- twice_as_long(Xs,Ys) .
The predicate will succeed when you get to the simple base case, and fail otherwise.

Related

Repeat and Double elements in lists in Prolog

how can I write two predicates that are described below.
1) Define the double(X,Y) predicate, which is true if the list Y contains each of the elements X
repeated twice. Example: double([a,b],[a,a,b,b]) is true.
2) Define the predicate repeat(X,Y,N), which is true if the list Y contains each of the elements X
repeated N times. For example, for the question repeat([a,b],[a,a,a,b,b,b],3), Prolog answers true.
Could you give me the example of those predicates?
If you have repeat/3 you have double/2.
and thus:
multiple(X,N,R) :-
length(R,N),maplist(=(X),R).
repeat(Li,Lo,N) :-
maplist({N}/[Xi,Xo]>>multiple(Xi,N,Xo),Li,Nested),flatten(Nested,Lo).
But it doesn't run backwards due to the flatten/2 I think. Can that be improved?
double([], []).
double([X|Y], [X,X|Z]) :- double(Y,Z).
remove_if_same(_,R,0,R):- !.
remove_if_same(X,[X|Y],N,R) :- Nm1 is N-1,remove_if_same(X,Y,Nm1,R).
repeat([],[],_).
repeat([X|Xr],Y,N) :- remove_if_same(X,Y,N,R), repeat(Xr,R,N).
How double works?
If you've got two empty lists, then that is true, there is nothing to double from the first argument.
Otherwise, you're taking the head from the first list, and 2 head elements from the second list. If all these are the same (so if all are X) you're checking with recursion rest of elements, so accordingly Y and Z. So you'll check once again if lists are empty and so on, and if on any of the steps sth is not possible you return false.
About the repeat predicate, it's quite similar in reasoning.
2 things that I should explain:
The ! mark will make that the command-line interface(like swipl) will not search for other results of the remove_if_same. It would work same if we pass it to the repeat.
remove_if_same statement uses the accumulator (the 4th argument) to return at the end of the search the list without N same elements.

Why am I getting Type error: `[]' expected, found `[21,3,4,5,6,7,8]' (a list) ("x" must hold one character) with maplist/3 prolog?

I am new to Prolog. I want a predicate that takes a list, process it with maplist/3 creating a corresponding list with zeros in place of numbers less than mean and 1 for number above the mean. I then want a second predicate to sum the 1's to find out how many numbers are above the mean. This second predicate then returns this number which corresponds to total numbers above the mean.
I know the code below works fine:
numAtOrAboveMean(Mean, Num, Val) :- Num > Mean -> Val is 1; Val is 0.
maplist(numAtOrAboveMean(Mean), [], List), sumlist(List, Below).
When I modified it to this, I get a type erros that expected [] but found a list. The comments correspond to how I think the predicate behavior is.
nGMean(Mean, Num, Val) :- Num > Mean -> Val is 1; Val is 0.%sorts list
nGMean([], _ , []). %takes a list, a constant, relates to a list
nGMean(L, Mean, List) : - maplist(nGMean(Mean), L, List). %maplist call sort
Then to sum I will use a second predicate. Something like this:
sumtotal(L,V) :- mean(L, M), M2 is M, nGMean(L, M2, List), sum(List, V).
Which is not working probably mostly because nGMean is throwing an error. nGMean full error is shown below:
So my question is, why am I getting that type error on the nGMean predicate?
Edit -As requested in comments below is the entire thing. As I explained that is the only part because I am testing it separately.
Thank you for answers. Next time I will post complete code.Or make clear that I just want to trouble shoot one predicate.
Maplist for numAtOrAboveMean
Full Pic of code on Editor
You should post complete code that can just be copied and run. In what you have posted, mean/2 and sum/2 are not defined.
(Addition:) the reason for the error seems to be that you are comparing a value and a list (2<[2,3|...]). The reason this happens is because your first clause for nGMean/3 has Mean as first parameter, whereas the other clauses has the list, i.e. the list becomes Mean which is used in the comparison (Num > Mean). I'm not sure how > becomes <.
Also, calling maplist/3 on an empty list does not make sense.
A recursive predicate should have two clauses. A recursive clause that (typically) does something with the head of the list and then calls recursively on the tail, and a base case (empty list).
nGMean([Num|Nums],Mean,[Val|List]) :-
( Num > Mean ->
Val = 1
; Val = 0 ),
nGMean(Nums,Mean,List).
nGMean([],_,[]).
With this definition I get the same output as your first two lines above, so I believe this is what you wanted.
(Earlier addition: you only need to use is when the right-hand side contains mathematical calculations. To just set a value, = is fine.)

Average of a student using prolog

I know this looks a bit silly, but I'm trying to find the average of a given student using Prolog, here's my piece of code:
score( jason , math101 , 90 ).
score( sami , math102 , 67 ).
score( smith , phys101 , 82 ).
score( sami , chem101 , 88 ).
do(X,S,I) :-
score(X,_,B) ,
write(B) ,
S is S+B ,
I is I+1 ,
write(I) ,
fail.
start :-
read(X) ,
do(X,0,0)
.
I'm trying to do it using recursion, the problem is that I (stands for index) and S(stands for sum) wont increment ! what did I do wrong? thanks !
You code has some problems:
Expressions like S is S+B don't work in prolog. Once a variable is unified with a term (assigned a value), it ceases to be variable: it becomes that with which it was unified. Hence the name unification.
Your do/3 predicate will always fail. It looks like you are forcing backtracking via fail/0 and expecting your counters to be incremented as you go. Prolog doesn't work like that: As you backtrack, unification is undone, so even if you'd managed to increment your S and B variables, the increment would have been rolled back as you backtracked through it.
You are invoking your do/3 predicate with S and B already unified to a value (0). So your is/2 expressions are the exact equivalent of this:
0 is 0+B , % S is S+B ,
0 is 0+1 , % I is I+1 ,
You can see how that won't work.
What you need to do is collect the scores for each student as a list. Since you can only get them one at a time via backtracking, building that list is a little difficult. If you tried to build that list via recursion, on each recursion you find yourself starting over from ground zero. Conveniently, Prolog offers a set of predicates for collecting data from all solutions to a goal as a list:
setof/3 finds the set of all solutions to a goal, presenting them as a list.
findall/3 finds the bag of all solutions to a goal, presenting them as a list.
bagof/3 finds the bag of all solutions to a goal, presenting them as a list.
Notes: findall/3 and bagof/3 are similar but different: to quote the docs, "findall/3 is equivalent to bagof/3 with all free variables bound with the existential operator ^". You'll probably need to play with it a bit to grok just what that means. The difference between set and bag is that by definition, sets are unique and have no duplicates. Bags, on the other hand are not necessarily unique and may contain duplicates.
At any rate, you're interested in bagof/3.
The first thing to do is decompose your problem into simple pieces. The first thing you need to do is compute the average score for a single student. to do that you need:
The student's name,
The list of individual scores for that student, and
a way to compute the arithmetic mean from a list of numbers.
So, let's tackle each of this in turn. The 1st two problems are solved with bagof/3. This:
bagof(Score,score(Student,_,Score),Scores) .
will find the list of scores for a student, unifying Student with the student's name and Scores with the list of individual scores. On backtracking it will successively do the same for all students.
That takes care of the first two simple problems. The 3rd problem isn't much more difficult. Here we will introduce two concepts that are integral to prolog: recursion and the use of accumulators to develop a value. we have to do this because as noted, in Prolog, all variables are local and can only be assigned a value once. So to develop a value, we have to recursively carry the state around that we need. A common idiom is the use of helper predicates that do all the work and are invoked by the "public" predicate that the user actually uses.
We can compute the arithmetic mean of a list of numbers like this:
compute_mean( Ns, M ) :- % to compute the mean of a list of numbers,
compute_mean( Ns , 0 , 0 , M ) % - just invoke the helper with the counters seeded as zero.
. % Also easy!
compute_mean( [] , S , N , M ) :- % if the source list is exhausted,
N > 0 , % - and we have a non-zero N (division by zero being undefined),
M is float(S) / float(N) % - compute the arithmetic mean, casting both counters to floats
. % otherwise...
compute_mean( [X|Xs] , S , N , M ) :- % if the source list isn't yet exhausted,
S1 is S+X , % - add the data point to the running total,
N1 is N+1 , % - increment the count of data points,
compute_mean(Xs,S1,N1,M) % - and recurse down on the tail of the list.
. % Easy!
That's all we need to compute the average score for a student, and on backtracking, successively do so for all students. Just put the two things together:
mean_score( Student , Mean ) :- % to compute the mean score for a student,
bagof( Mean , score(Student,_,Score) , Scores ) , % - get the bag of the scores for the student
compute_mean( Scores , Mean ) % - and then compute the mean score
. % Easy!
Once you have that, you could then just say something like this:
list_mean_scores_for_all_students :-
mean_score(Student,Mean) ,
write( Student:Mean) , nl ,
fail .
list_mean_scores_for_all_students .
The problem with the above is that list_mean_scores_for_all_students/0 will always succeed, even if something untowards happened. So, you might want to use findall/3 to collect them all at once and then process them.
Then we can say:
list_mean_scores_for_all_students :-
findall( Student:Mean , mean_score(Student,Mean) , Results ) ,
log_results(Results)
.
log_results( [] ) .
log_results( [S:M|Xs] ) :-
write(S:M),nl ,
log_results(Xs)
.
If something fails, list_means_scores_for_all_students/0 will fail.

Taking out the 2nd to last element - Prolog

I'm very new to Prolog and am trying to figure out exactly what is happening with this (function?) that takes out the 2nd to last element in a list.
remove([],[]).
remove([X],[X]).
remove([_,X],[X]).
remove([X|Xs], [X|Ys]) :-
Xs = [_,_|_],
remove(Xs,Ys).
I'm familiar with pattern matching, as I've done a little work in SML. The first one is clearly the base case, returning the empty list when we break it down. The second returns the same variable when there is only one left. The third looks as if it returns the last element, disregarding the 2nd to last? As for the inductive case, it will attach the head of the list to the new list if ...... (This is where I get completely lost). Could anyone explain what's happening in this function so I can have a better understanding of the language?
Elaborating on CapelliC's explanation:
remove([],[]).
An empty list is an empty list with the second-to-last element removed.
remove([X],[X]).
A single-element list is itself with the second-to-last element removed.
remove([_,X],[X]).
A two-element list with the second to last element removed is a list of one element consisting of the last element of the two-element list.
remove([X|Xs], [X|Ys]) :-
Xs = [_,_|_],
remove(Xs,Ys).
The second list is the first list with the second element removed, and share the same first element, IF:
The tail of the first list consists of at least two elements, AND
The tail of the second list is the tail of the first list with the second to last element removed
A set of clauses is a predicate, or procedure.
All first three are base cases, and the recursive one copies while there are at least 3 elements in the first list.
I would describe the behaviour like 'removes pre-last element'.
So, how to declaratively read
remove([X|Xs], [X|Ys]) :-
Xs = [_,_|_],
remove(Xs,Ys).
Most important is that you first realize what the :- actually means.
Head :- Body.
It means: Whenever Body holds, we can conclude that also Head holds. Note the rather unintuitive direction of the arrow. It goes right-to-left. And not left-to-right, as often written informally when you conclude something. However, the error points into the direction of what we get "out of it".
To better see this, you can enter Body as a query!
?- Xs = [_,_|_], remove(Xs,Ys).
Xs = [A, B], Ys = [B]
; Xs = [A, B, C], Ys = [A, C]
; ... .
So we get all answers, except those where Xs has less than two elements.
Please note that procedurally, things happen exactly in the other direction - and that is very confusing to beginners. Even more so, since Prolog uses two "non-traditional" features: chronological backtracking, and variables - I mean real variables, meaning all possible terms - not these compile time constructs you know from imperative and functional languages. In those languages variables are holders for runtime values. Concrete values. In Prolog, variables are there at runtime, too. For more to this, see Difference between logic programming and functional programming
There is also another issue, I am not sure you understood. Think of:
?- remove(Xs, [1,2]).
Xs = [1, A, 2]
; false.
What is removed here? Nothing! Quite the contrary, we are adding a further element into the list. For this reason, the name remove/2 is not ideal in Prolog - it reminds us of command oriented programming languages that enforce that some arguments are given and others are computed. You might at first believe that this does not matter much, after all it's only a name. But don't forget that when programming you often do not have the time to think through all of it. So a good relational name might be preferable.
To find one, start with just the types: list_list/2, and then refine list_removed/2 or list__without_2nd_last/2.
Annotated:
remove( [] , [] ) . % removing the 2nd last element from the empty list yields the empty list
remove( [X] , [X] ) . % removing the 2nd last element from a 1-element list yields the 1-element list.
remove( [_,X] , [X] ) . % removing the 2nd last element from a 2-element list yields the tail of the 2-element list
remove( [X|Xs] , [X|Ys] ) :- % for any other case...
Xs = [_,_|_], % * if the tail contains 2 or more elements, the list is 3 elements or more in length
remove(Xs,Ys). % we simply prepend the head of the list to the result and recurse down.
It should be noted that the last clause could re-written a tad more clearly (and a little more succinctly) as:
remove( [X1,X2,X3|Xs] , [X1|Ys] ) :- % for any other case (a list of length 3 or more)
remove([X2,X3|Xs],Ys). % we simply prepend the head of the list to the result and recurse down.
Or as
remove( [X1|[X2,X3|Xs]] , [X1|Ys] ) :- % for any other case (a list of length 3 or more)
remove([X2,X3|Xs],Ys). % we simply prepend the head of the list to the result and recurse down.

Sum of a list in prolog

I'm reading 'the art of prolog' book and I found an exercise that reads 'Define the relation sum(ListOfIntegers,Sum) which holds if Sum is the sum of the ListOfIntegers, without using any auxiliary predicate' .I came up with this solution:
sum([],Sum).
sum([0|Xs], Sum):-sum(Xs, Sum).
sum([s(X)|Xs], Sum):-sum([X|Xs],s(Sum)).
Which does not work exactly as I would want it to.
?- sum([s(s(0)),s(0),s(s(s(0)))],X).
true ;
false.
I was expecting X to be
s(s(s(s(s(s(0))))))
I thought that the problem is that I have to 'initialize' Sum to 0 in the first 'iteration' but that would be very procedural and unfortunately I'm not quite apt in prolog to make that work.
Any ideas or suggestions?
Your first clause should read
sum([], 0).
With that change, the vacuous true return goes away and you're left with one problem: the third clause reverses the logic of summation. It should be
sum([s(X)|Xs], s(Sum)) :- sum([X|Xs], Sum).
because the number of s/1 terms in the left argument to sum/2 should be equal to the number of them in the right argument.
The best way to localize the problem is to first simplify your query:
?- sum([0],S).
true.
?- sum([],S).
true.
Even for those, you get as an answer that any S will do. Like
?- sum([],s(s(0))).
true.
Since [] can only be handled by your fact, an error must lie in that very fact.
You stated:
sum([], Sum).
Which means that the sum of [] is just anything. You probably meant 0.
Another error hides in the last rule... After fixing the first error, we get
?- sum([0],Sum).
Sum = 0.
?- sum([s(0)],Sum).
false.
Here, the last clause is responsible. It reads:
sum([s(X)|Xs], Sum):-sum([X|Xs],s(Sum)).
Recursive rules are relatively tricky to read in Prolog. The simplest way to understand them is to look at the :- and realize that this should be an arrow ← (thus a right-to-left arrow) meaning:
provided, that the goals on the right-hand side are truewe conclude what is found on the left-hand side
So, compared to informal writing, the arrows points into the opposite direction!
For our query, we can consider the following instantiation substituting Xs with [] and X with 0.
sum([s(0)| [] ], Sum) :- sum([0| []],s(Sum)).
So this rule now reads right-to-left: Provided, sum([0],s(Sum)) is true, ... However, we do know that only sum([0],0) holds, but not that goal. Therefore, this rule never applies! What you intended was rather the opposite:
sum([s(X)|Xs], s(Sum)):-sum([X|Xs],Sum).
I'm not really following your logic, what with all the seemingle extraneous s(X) structures floating about.
Wouldn't it be easier and simpler to do something like this?
First, define your solution in plain english, thus:
The sum of an empty list is 0.
The sum of a non-empty list is obtained by adding the head of the list to the sum of the tail of the list.
From that definition, the prolog follows directly:
sum( [] , 0 ) . % the sum of an empty list is 0.
sum( [X|Xs] , T ) :- % the sum of an non-empty list is obtained by:
sum( Xs , T1 ) , % - first computing the sum of the tail
T is X + T1 % - and then, adding that the to head of the list
. % Easy!

Resources