Reputation: 59
Given a list of lists of integers, e.g. [[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]]
, I want to go over each sublist and count how many of them sum to 15. In this case that would be 1, for the sublist [3,10,2]
.
I am aware of the predicate aggregate_all/3
, but I'm having trouble writing a predicate to check each element of the list, what I have now is something like
fifteens([X|Xs]) :-
sum_list(X, 15),
fifteens(Xs).
and within another predicate I have:
aggregate_all(count, fifteens(Combinations), Value).
where Combinations
is the list of lists of integers in question.
I know my fifteens
predicate is flawed since it's saying that all elements of the nested list must sum to 15, but to fix this how do I take out each element of Combinations
and check those individually? Do I even need to? Thanks.
Upvotes: 0
Views: 316
Reputation: 15316
This is a job for ... foldl/4
. Functional programming idioms in logic programming languages? Yes, we can!
First, summing the summable values of a list:
sum_them(List,Sum) :-
foldl(sum_goal,List,0,Sum).
sum_goal(Element,FromLeft,ToRight) :-
must_be(number,Element),
must_be(number,FromLeft),
ToRight is Element+FromLeft.
Then, counting the ones that sum to 15:
count_them(List,Count) :-
foldl(count_goal,List,0,Count).
count_goal(Element,FromLeft,ToRight) :-
must_be(list(number),Element),
must_be(number,FromLeft),
sum_them(Element,15) -> succ(FromLeft,ToRight) ; FromLeft = ToRight.
Does it work? Let's write some unit tests:
:- begin_tests(fifteen_with_foldl).
test("first test",true(R==1)) :-
count_them([[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]],R).
test("test on empty",true(R==0)) :-
count_them([],R).
test("test with 2 hist",true(R==2)) :-
count_them([[15],[],[1,1,1,1,1,10]],R).
:- end_tests(fifteen_with_foldl).
And so:
% PL-Unit: fifteen_with_foldl ... done
% All 3 tests passed
true.
Upvotes: 1
Reputation: 12972
First of all your fifteens/2
predicate has no because for empty list and thus it will always fails because due to the recursion eventually fifteens([])
will be called and fail.
Also you need to change completely the definition of fifteens, currently even if you add base case, it says check ALL elements-sublists to see if they sum to 15. That's Ok but I don't see how you could use it with aggregate.
To use aggregate/3
you need to express with fifteens/2
, something like: for every part of my combinations list check separately each sublist i.e each member:
ifteens(L) :-
member(X,L),
sum_list(X, 15).
Now trying:
?- aggregate_all(count, ifteens([[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]]), Value).
Value = 1.
Upvotes: 2