Prolog compare list elements to Head element - prolog

I have predicate which shows all numbers which are smaller than 10.
small([H|T],H):- H=<10.
small([_|T],X):-small(T,X).
It is quite simple. But how should I change it so that I could compare every next item to the first element?
For example, ?- small([4,2,3,9,1,0,12],X). would show X=2;X=3;X=1;X=0 ?

You can split the problem in two procedures, one that takes the first element and then applies the recursion over the remaining elements of the list and checks whether each item is less than or equal to the first element:
small([M|T],X):- small1(T, M, X).
small1([H|_], M ,H):- H =< M.
small1([_|T], M, X):- small1(T, M, X).

Related

Prolog lists inside the lists

I'm new to Prolog and what I'm trying to achieve is to define the tower-like type which contains lists of natural numbers. Each "floor" is supposed to have the same amount of "apartments" and I don't know how can I check it.
Assuming with "type" you mean a structure which can hold your data, I suggest using a list of lists, like this:
[[101,102,103],[201,202,203],[301,302,303]]
So now you need to check if each "floor" (sublist), has the same amount of elements. Counting the elements in a list is rather simple:
len([],0).
len([H|T],N1) :-
len(T,N),
N1 is N+1.
Which reads: an empty list [] has 0 elements. A list which can be splittet into a head element H and a rest list T has the length N1, if the length of T is N and N1 is calculated by N+1.
So what is missing? You need to go through all your sublists and get the length of the sublist. If the length of two sublists are different, then fail. If you reach the end with allways the same number, success. So the pattern would look like this:
isHotel(R):-
isHotel(R,_).
Add a placeholder for the counting, actual number of the second argument does not matter at this point.
isHotel([],_) .
If you got to a point where there are no floors left, accept for any number.
isHotel([CurrentFloor|T],N) :-
len(CurrentFloor,N),
isHotel(T,N).
If there are floors left (floorlist can be devided into the current floor CurrentFloor and all floors above T), count the rooms at this floor and go check with the left over floors T. The first time this is runs, the number N of rooms will be calculated and forwarded to every other call - therefore for the first time the calculation is just a calculation, in every other ecxecution it is a check as well. This leads to rejects if two lusts do not match in size. Please note this code does not check if the input is a list of lists of numbers.
Checked with SWISH:
?- isHotel([[101,102,103],[201,202,203],[301,302,303]]).
true.
?- isHotel([[101,102,103],[201,202,203],[301,302,3,03]]).
false.
?- isHotel([]).
true.

Display all list elements that greather than N - Prolog

I want to Display all list elements that larger than N, my code like
member2(X, [X|_]).
member2(X, [_|T]) :- member2(X,T), X > T, write(X).
Why would you state member2(X, [X|_]) ? Its not the last element yet. You should only stop when there are no more elements to check: member2(X,[]).
And generally:
member2(X,[Y|Ys]) :- (X >= Y -> display(Y), nl; true), member2(X,Ys).
This way you will display all the Ys that are smaller or equal to X.
In your code, T was a list of elements. You cannot compare it to a single elememt X.
You could use include/3 to perform the filtering. For example:
include(<(5),[7,3,2,6,4,5,8],Output).
will unify Output with [7,6,8] (the elements in the second argument that are greater than the number used in the first argument).

Generate a 3d List

I am trying to make a List of Lists of Lists without values. If N_meses = 4 I want List =[[[A,B,C,D]]].
I get what I want ( List = [[[]]] ) but every lists have the same values as you can see in the print I attached. How can I change this code so every lists have a different "value"?
I am doing this
generate_table(Num_investigadores, Num_actividades, N_Meses, Tabela) :-
length(Row, Num_actividades),
length(X,N_Meses),
maplist(=(X), Row),
length(Tabela, Num_investigadores),
maplist(=(Row), Tabela).
The culprit is in essence the:
%% ...
maplist(=(X), Row),
%% ...
Here you basically defined a list X, and then you set with maplist/2 that all elements in Row are unified with that X. In the unification process. This thus means that all the elements of Row will in essence point to the same list.
Nevertheless, I think it would definitely help if you make the predicate less ambitious: implement helper predicates and let each predicate do a small number of things.
We can for example first design a predicate lengthlist/2 that is the "swapped" version of length/2, and thus has as first parameter the length and as second parameter the list, like:
lengthlist(N, L) :-
length(L, N).
Now we can construct a predicate that generates a 2d rectangular list, for example:
matrix(M, N, R) :-
lengthlist(M, R),
maplist(lengthlist(N), R).
here we thus first use lengthlist to construct a list with N elements, and then we use maplist/2 to call lengthlist(N, ...) on every element, such that every element is unified with a list of N elements. We thus construct a 2d list with M elements where every elements is a list of N elements.
Then finally we can construct a 3d tensor:
tensor3(L, M, N, T) :-
lengthlist(L, T),
maplist(matrix(M, N), T).
Here we thus construct an L×M×N tensor.
We can in fact generalize the above to construct a arbitrary deep cascade of lists that is "rectangular" (in the sense that for each dimension, the lists have the same number of elements), but I leave this as an exercise.

Prolog - return second to last list element

puzzling over a problem of trying to return the second to last element in a list written in Prolog. This language is interesting to use but I'm having trouble getting my head wrapped around it. Here is what I have:
secondLast([X], X).
secondLast(X, [Y], X) :- secondLast(Y, K).
secondLast(X, [Y|Z], K) :- secondLast(Y, Z, K).
secondLast([X|Z], Ans) :- secondLast(X, Z, Ans).
so calling secondLast([a, b, c, d], X).
X should equal c.
Any ideas?
Thanks!
you should apply pattern matching:
secondLast([X,_], X).
secondLast([_|T], X) :- secondLast(T, X).
Can be just:
secondLast(L, X) :-
append(_, [X, _], L).
Sergey and CapelliC have offered two nice solutions to the problem. Let's have a look to see what's wrong with the original:
1) secondLast([X], X).
2) secondLast(X, [Y], X) :- secondLast(Y, K).
3) secondLast(X, [Y|Z], K) :- secondLast(Y, Z, K).
4) secondLast([X|Z], Ans) :- secondLast(X, Z, Ans).
In Prolog, since it is about defining relations between entities with predicates, not defining functions, it helps to describe what a predicate means in terms of, "Something is true if some other things are true". The if in Prolog is expressed as :-.
We'll look at clause #4 since this appears to be your main clause. This one says, Ans is the second to last element of [X|Z] if Ans is the second to last element of Z with X as the head. It's unclear what this 3-argument version of secondLast means. However, if the list is 3 or more elements, it seems clear that X will become irrelevant (as will be seen in clauses 2 and 3).
Clause #1 says, X is the second to last element in list [X]. However, the element X is the last and only element in the list [X]. So this clause is logically incorrect.
Clauses #2 is a bit confusing. It introduces a variable in the clause, K, which is only used once and not defined or used anywhere else in the clause. It also ignores X because, as described above, it has become irrelevant since it's no longer a candidate for second-to-last element. Prolog has given you a warning about singleton elements K and X, which is similar to the warning in C that a variable is "defined but never used" or "is assigned a value that is never used". Clause #3 has the same issue.
In all that, I think I can see what you were trying to do, which is to say that, Ans is second to last element of [X|Z] if there's one more element after X, which would be true, but would be limited to being correct if the list [X|Z] is a 2-element list. In other words, your main clause almost assumes that the answer is ultimately X. If it isn't, it attempts to introduce a new candidate, Y in clauses 2 and 3, but this candidate has no way to "make it back" to the original, main clause.
I'll go back now to CapelliC's solution and describe how one comes to it:
1) secondLast([X,_], X).
2) secondLast([_|T], X) :- secondLast(T, X).
The first clause says, X is the second to last element in the 2-element list, [X,_] which is true. And we don't care what the last element is so we just call it, _. We could have called it Y ([X,Y]), but then Prolog would warn about a singleton variable since we don't need or use Y.
The second clause says, X is the second to last element of list [_|T] if X is the second to last element of the tail, T. This is also true of any list that is 3 or more elements. That's fine since the base case, clause one, takes care of the 2-element list. Clause two will, recursively, reduce down to clause one and finally succeed with the right answer. In this second clause, if X is taken from T, then we don't care what the head of the list is since it has become irrelevant, so we use _ as the head in this case (this corresponds to the X in your original clause #4).
In Sergey's answer:
secondLast(L, X) :-
append(_, [X, _], L).
This says, X is second to last element in list L if L is a two element list with X as the first element ([X,_]) appended to the end of some other list (_). Note again that we're using _ for the variables which will have values but we don't care what those values are in this case. So, for example: 2 is the second to last element of [1,2,3] if [1,2,3] is [2,_] appended to some other list and it is: if you append [2,3] to [1] you get [1,2,3].

Passing results in prolog

I'm trying to make a function that has a list of lists, it multiplies the sum of the inner list with the outer list.
So far i can sum a list, i've made a function sumlist([1..n],X) that will return X = (result). But i cannot get another function to usefully work with that function, i've tried both is and = to no avail.
Is this what you mean?
prodsumlist([], 1).
prodsumlist([Head | Tail], Result) :-
sumlist(Head, Sum_Of_Head),
prodsumlist(Tail, ProdSum_Of_Tail),
Result is Sum_Of_Head * ProdSum_Of_Tail.
where sumlist/2 is a SWI-Prolog built-in.
Usage example:
?- prodsumlist([[1, 2], [3], [-4]], Result).
Result = -36.
The part "it multiplies the sum of the inner list with the outer list" isn't really clear, but I believe you mean that, given a list [L1,...,Ln] of lists of numbers, you want to calculate S1*..*Sn where Si is the sum of the elements in Li (for each i).
I assume the existence of plus and mult with their obvious meaning (e.g. plus(N,M,R) holds precisely when R is equal to N+M). First we need predicate sum such that sum(L,S) holds if, and only if, S is the sum of the elements of L. If L is empty, S obviously must be 0:
sum([],0).
If L is not empty but of the form [N|L2], then we have that S must be N plus the sum S2 of the elements in L2. In other words, we must have both sum(L2,S2) (to get S2 to be the sum of the elements of L2) and plus(N,S2,S). That is:
sum([N|L2],S) :- sum(L2,S2), plus(N,S2,S).
In the same way you can figure out the predicate p you are looking for. We want that p(L,R) holds if, and only if, R is the product of S1 through Sn where L=[L1,...,Ln] and sum(Li,Si) for all i. If L is empty, R must be 1:
p([],1).
If L is not empty but of the form [LL|L2], then we have that R must be the product of 'S', the sum of the elements of LL, and 'P', the product of the sums of the lists in L2. For S we have already have sum(LL,S), so this gives us the following.
p([LL|L2],R) :- sum(LL,S), p(L2,P), mult(S,P,R).
One thing I would like to add is that it is probably not such a good idea to see these predicates as functions you might be used to from imperative or functional programming. It is not the case that sumlist([1,..,n],X) returns X = (result); (result) is a value for X such that sumlist([1,...,n],X) is true. This requires a somewhat different mindset. Instead of thinking "How can I calculate X such that p(X) holds?" you must think "When does P(X) hold?" and use the answer ("Well, if q(X) or r(X)!") to make the clauses (p(X) :- q(X) and p(X) :- r(X)).
Here is a rewrite of Kaarel's answer (that's the intention anyway!) but tail-recursive.
prodsumlist(List, Result) :-
xprodsumlist(List,1,Result).
xprodsumlist([],R,R).
xprodsumlist([Head|Rest],Sofar,Result) :-
sumlist(Head, Sum_Of_Head),
NewSofar is Sofar * Sum_Of_Head,
xprodsumlist(Rest, NewSofar, Result).

Resources