currently learning about logic in computing as well as the foundations of Prolog. Please bear with me in my fumbling speech!
I've been faced with a problem where, given an query of a list of lists containing integers, one must devise a way to calculate the highest sum of all the lists using recursion.
For example:
?- getSum([[1,3,6],[9,5,2],[3,4,7]], X).
X = 16.
Should be the output.
I believe the correct way to find the sum of a list is:
sum([],0).
sum([Head|Body], Output) :- sum(Body, BodySum),
Output is Head + BodySum.
However I need to compare the sums before displaying the result, do I have to call to another conditional inside the sum recursion?
Thanks
Several comments first:
Naming: you probably want to name your predicate so that it is obvious what it does. And, Prolog prefers names_like_this to namesInCamelCase. So maybe, call it max_sublist_sum, maybe?
Implementation: in your question you say, "using recursion". Must you use recursion, or do you think that using recursion is your only choice? Since this is a trivial problem, it can be solved using library predicates and avoiding recursion altogether. For example, to find the sums of each sub-list:
maplist(sum_list, List_of_lists, Sums)
Now you have reduced your list of lists to a list of sums. You can find the largest of a list of numbers:
max_list(List_of_numbers, Max_number)
So, your problem becomes:
?- maplist(sum_list, [[1,3,6],[9,5,2],[3,4,7]], Sums),
max_list(Sums, Max_sum).
Sums = [10, 16, 14],
Max_sum = 16.
Now, it may seem as a good exercise to implement each of sum_list/2 and max_list/2, but a good starting point would be to look at the library implementation of these two. Same goes for the definition of maplist/3
You can also intertwine the two:
max_sublist_sum0([H|T], Max) :-
sum_list(H, Sum),
max_sublist_sum0_(T, Sum, Max).
max_sublist_sum0_([], Max, Max).
max_sublist_sum0_([H|T], M0, Max) :-
sum_list(H, Sum),
( Sum > M0
-> max_sublist_sum0_(T, Sum, Max)
; max_sublist_sum0_(T, M0, Max)
).
The helper predicate max_sublist_sum_/3 is in practice a fold:
max_sum(L, M0, M) :-
sum_list(L, Sum),
M is max(M0, Sum).
max_sublist_sum1([H|T], Max) :-
sum_list(H, Sum),
foldl(max_sum, T, Sum, Max).
However, this is still much more code than the original suggestion:
max_sublist_sum(L, M) :-
maplist(list_sum, L, S),
max_list(S, M).
Interestingly, on my computer this last version is also fastest for somewhat larger lists, and takes about the same time as the faster of the other two.
You don't need to call another condition inside the recursion, you have to decompose the problem into calculating the sum and the maximum. Following a classic implementation of a max predicate, where the second argument is the current maximum, this would be something like:
max([], X, X).
max([H|T], X, Y) :-
sum(H, S),
S < X,
max(T, X, Y).
max([H|T], X, Y) :-
sum(H, S),
S >= X,
max(T, S, Y).
getSum(L, X) :- max(L, 0, X).
You could make this code more efficient and elegant by using an if-construct or a cut, I leave this to you.
Related
I want to write a code that multiplies lists representing a number, like:
?- times([1,1,1], [1,1], Res).
Res = [1,1,1,1,1,1].
times([], _, []). % base case
times([_|T], Lis, [Lis|H]) :-
times(T, Lis, H).
I already have the code above and it kinda does what I want but not really. For example when asking:
?- times([1,1,1], [1,1], Res)
Res = [[1,1],[1,1],[1,1]].
The idea is there, but I just don't know how to fix that, I understand why it's happening (I'm adding a list as head), so I just wondered if anybody could help me.
Thanks in advance.
[Lis|H] will use Lis as first element, regardless whether Lis is a list or not. You should take a look at append/3 [swi-doc] for example to append two lists:
times([], _, []).
times([_|T], Lis, R) :-
append(Lis, H, R),
times(T, Lis, H).
I have a matrix, where every element should be unique. To be honest, every element can take an integer value in [1, 19], but I got confused on handling list with variables with length(List, 3), so for now I have this:
matrix([[a,b,c],[d,e,f],[g,h,i]]).
row(M, N, Row) :-
nth1(N, M, Row).
column(M, N, Col) :-
transpose(M, MT),
row(MT, N, Col).
get_row(N, Row) :-
matrix(M),
row(M, N, Row).
diff_matrix(M) :-
matrix(M),
foo(M).
foo([]).
foo([H|T]) :-
length(H, Len),
write(Len),
foo(T).
different_from([], _).
different_from([H|T], E) :-
E \= H,
different_from(T, E).
Any idea to proceed with this code, or maybe another approach? I mean if my attempt is not good enough, I do not have problem replacing it.
EDIT:
I have atoms, because I do not know how to constraint variables
inside [1, 19], so I am trying to make it work with atoms, for now!
I want my code to test if matrix contains unique elements, i.e., for
every element found in the matrix, there is no duplicate element in
the matrix.
So far, I have only predicates that should help, nothing more, since
I am stuck.
A possible query: diff_matrix([[1,2,3],[4,5,6],[7,8,9]]).
A very compact, maybe inefficient, method based on indexing
rc(M,R,C,E) :- nth1(R,M,Row),nth1(C,Row,E).
diff_matrix(M) :-
forall((rc(M,I,J,X),rc(M,U,V,Y)), ((I\=U;J\=V)->X\=Y;true)).
edit
rc/4 is the relation among matrix M, row index R (1 based), column index C, and element E.
forall(Cond,Action) documentation states:
For all alternative bindings of Cond, Action can be proven.
So we can read diff_matrix/1 as
for all elements X (let's say, M[I,J]) and Y (M[U,V]) either I=U and J=V or X \= Y (doesn't unify)
I am studying prolog and was wondering if anybody give me guidance on how to go about doing this question, It's the first of many in this area and knowing how to do this question will really help me progress. Thank-you in advance.
Using Prolog define a predicate mapof(K, M, V) such that, when invoked with K instantiated to a key, and M instantiated to a mapping, mapof will instantiate the variable V to the value (or one of the values) associated with K in mapping M. The predicate should fail if K does not appear as a key in mapping M.
It really depends how you want to represent your "mapping". In Prolog, a table of facts is the most obvious approach. For two mappings m and n:
m(a, 1).
m(b, 2).
m(c, 3). % and so on
n(a, foo).
n(b, bar).
n(c, baz). % and so on
Then, your mapof would be something along the lines of:
mapof(K, m, V) :- m(K, V).
mapof(K, n, V) :- n(K, V).
or maybe:
mapof(K, M, V) :- call(M, K, V).
A list can be used to represent a mapping, as shown by #Yasel, but a list [a, b, c] in Prolog is a nested term like .(a, .(b, .(c, []))). You don't usually represent an associative array as a singly linked list, right?
In SWI-Prolog there is a library that is better than using a simple list for a backtrackable associative array represented as a Prolog term: library(assoc). With it, you can do:
mapof(K, M, V) :- gen_assoc(K, M, V).
This library represents the associative array as an AVL tree. You can find in the SWI-Prolog code source two more associative array implementations: one using RB-trees, and one that uses non-backtrackable RB-trees.
All three libraries mentioned here are probably more efficient than a simple list of key-value pairs [k1-v1, k2-v2...] if your associative array has more than say around 100 key-value pairs in it. This doesn't mean that using a list of pairs and doing member(Key-Value, List_of_pairs) is wrong; it is the cheapest solution for simple cases.
Using the built-in predicate member/2 you can build your predicate mapof/3 like this:
mapof(K, M, V):- member((K,V), M).
Consult:
?- mapof(k1, [(k, a),(k1,b),(k2,c),(k1,d)], V).
V = b ;
V = d.
I'm trying to write a predicate is_multi(M), defined as:
every element of M has the form X / N, where X is an atom, and N is an integer greater than 0;
M does not contain two elements with the same atom, for what
is_multi([]).
is_multi([a / 2, b / 2]).
are satisfied, but
is_multi([a, b/2]).
is_multi([a/0, b/2]).
is_multi([a/2, 2/4])
is_multi([a/2, b/3, a/2])
is_multi([a/3, b/-4, c/1])
are not.
Here's what I have written so far:
is_multi(M) :- M = [].
is_multi(M) :-
M = [Head|Tail],
Head = X/N,
integer(N),
N > 0,
is_multi(Tail).
But it does not compare two elements if with the same atom. For example, is_multi([a/2, a/3]) is not satisfied. I got stuck for one day with this; could somebody give me some hints?
First, you can simplify your code considerably by moving some of your unifications from the body to the head.
is_multi([]).
is_multi([X/N|Tail]) :-
integer(N), N > 0,
is_multi(Tail).
Cleaning it up reveals one thing you're not doing here which is in your spec is checking that X is an atom. Fix by adding atom(X) to the body.
OK, so this takes care of the basic form, but doesn't ensure that the atoms do not repeat. The simplest thing to do would be to split this into two checks, one that checks that each item is well-formed, and one that checks that the list is well-formed. In fact, I would be inclined to use maplist/2 with a predicate that checks a single element. But all you really have to do is something like this:
is_valid([]).
is_valid([X/_|T]) :- is_valid(T), \+ memberchk(X/_, T).
This just says that the empty list is valid, and if the tail is valid, a list is valid if X over something doesn't occur in the tail.
If that's all you wanted, stop reading there. If you want to refactor, this is how I would approach it:
well_formed(X/N) :- atom(X), integer(N), N > 0.
no_repeating_numerators([]).
no_repeating_numerators([X/_|T]) :- no_repeating_numerators(T), \+ memberchk(X/_, T).
is_multi(L) :- maplist(well_formed, L), no_repeating_numerators(L).
Just to complete Daniel's instructive answer (+1 by me), I want to showcase how your task could be solved by means of some library predicates:
is_multi(L) :-
forall(select(E, L, R),
(E = A/N, atom(A), integer(N), N > 0, \+memberchk(A/_, R))).
question is:
when we key in mem([1,2,3,4,5]).
we will get the output as bellow:
odd=3
even=2
my coding is like that but cannot run. can help me check where is my mistake??
mem(X,[X|L]).
mem(X,[element|L]):-
mem([X,L]).
count([],L,L).
count([X|H],L1,L2):-
write(even),
X%2=0,nl,
write(odd),
X%2>=1,nl,
count([H],[X|L1],L2).
thanks for your helping.
The procedures you have written do two different things and don't actually belong together. mem/2 is equivalent to the usually builtin member/2 except that your definition contains an error: in the second clause element is an atom instead of a variable so it will not match other elements of the list. The usual definition is
member(X, [X|_]).
member(X, [_|L]) :- member(X, L).
Note that this definition will not only test if a term is an element of a list but can even be use to generate a list.
What exactly are you trying to do in count/3: split the list into two lists, one containing odd and the other containing even; or count the number of odd and even elements? The splitting could be done with something like:
count([], [], []).
count([X|L], O, E) :- X rem 2 =/= 0, count(L, [X|O], E).
count([X|L], O, E) :- X rem 2 =:= 0, count(L, O, [X|E]).
Note that =/= /2 and =:= / 2 force evaluation of arguments as arithmetic expressions while = /2 attempts to unify its arguments.
Counting the number of odds and evens can be done in a similar fashion, and is left as an exercise for the reader. :-)