i have a list of elements(tuples):
tuples(
[
(a,b,[1,3,5,7]),
(a,b,[9,11,13,15]),
(a,b,[17,19,21,23]),
(c,d,[0,2,4,6]),
(c,d,[8,10,12,14]),
(c,d,[16,18,20,22]),
(e,f,[100,200,300,400]),
(e,f,[500,600,700,800]),
(e,f,[900,1000,1100,1200])
]
).
How to group them so it becomes:
[
(a,b,[1,3,5,7,9,11,13,15,17,19,21,23]),
(c,d,[0,2,4,6,8,10,12,14,16,18,20,22]),
(e,f,[100,200,300,400,500,600,700,800,900,1000,1100,1200])
]
As we can see, we grouped the (a,b) (c,d) (e,f) and concatenated their respective lists.
thank you for the help.
How about this somewhat SQL-ish way:
Define a predicate to access data in the original deep and nasty datastructure (which uses conjunction (_,_) to create what looks like n-tuples but are actually "nearly" lists, except at the final position
of the backbone). On backtracking, it will pull out the individual
records one by one and present the information therein in the head variables:
some_tuple(V,W,Values) :-
tuples(Tuples),
member((V,W,Values), Tuples).
And then collect using a 2-level bagof/3 call.
solution(Bag) :-
bagof((V,W,FlatBagForVW), % will backtrack over V,W
SubBag^(bagof(L,some_tuple(V,W,L),BagForVW), % collect lists for a given V,W
flatten(BagForVW,FlatBagForVW)), % flatten it
Bag). % what we want
Done!
If you are in SWI-Prolog, first tell the toplevel printer to not elide long lists so much:
?-
set_prolog_flag(answer_write_options,[max_depth(100)]).
Then:
?-
solution(Bag).
Bag = [(a,b,[1,3,5,7,9,11,13,15,17,19,21,23]),
(c,d,[0,2,4,6,8,10,12,14,16,18,20,22]),
(e,f,[100,200,300,400,500,600,700,800,900,1000,1100,1200])].
A variation using foldl/4 and then library(yall):
'Grouping list elements based on values in list'(Gs) :-
tuples(Ts),
foldl([(X,Y,L),V0,V1]>>(
( append(N,[(X,Y,L0)|M],V0)
-> append(L0,L,L1)
; N=V0, L1=L
),
append(N,[(X,Y,L1)|M],V1)
)
,Ts,[],Gs).
a day after...
There is a bug going unnoticed, namely M remains unbound where a pair of keys (that is, X,Y in a tuple) is not present in V0 (have attempted to keep the variables naming coherent to the docs for foldl/4). A possible correction, that illustrates a single unification call (=/2) to perform multiple 'assignments' at once:
...
foldl([(X,Y,L),V0,V1]>>(
( append(N,[(X,Y,L0)|M],V0)
-> append(L0,L,L1)
; (N,M,L1)=(V0,[],L1)
),
append(N,[(X,Y,L1)|M],V1)
...
Still, it's not clear to me why the bug didn't materialized... for instance, here it's clearly visible...
?- append([1,2,3],[4,5,6|_],R).
R = [1, 2, 3, 4, 5, 6|_19156].
another way...
library(solution_sequences) is a recent addition to SWI-Prolog arsenal, providing more 'SQL like' constructs, for instance group_by/4:
by_group_by(Gs) :-
tuples(Ts),
findall((X,Y,All_xy), (
group_by([X,Y],L,member((X,Y,L),Ts),Lt),
append(Lt,All_xy)
),Gs).
The documentation is too much terse, but overall, the library is worth a try. There are some examples posted in SWI-Prolog discourse group, but I find the data used there boring and difficult to understand.
Note: I used the syntax [X,Y] for the free variables, to make clear the 'shape' of this important specification is unrelated to the pattern.
First we need to be able to find the list of unique A,B pairings in the list of tuples:
foo(X,A,B) :-
setof( p(A,B), C^member( (A,B,C), X ), T),
member( p(A,B), T).
/* 48 ?- tuples(_X), foo(_X,A,B).
A = a,
B = b ;
A = c,
B = d ;
A = e,
B = f. */
Next we collect the tuples for each pair of the A and B values:
bar(X,A,B,G) :-
foo(X,A,B), % for each unique (A,B,_) in X
findall( C, % find all Cs such that (A,B,C) is in X
member( (A,B,C), X),
CS),
append( CS,G). % and append them together
/* 66 ?- tuples(_X), bar(_X,A,B,G).
A = a,
B = b,
G = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23] ;
A = c,
B = d,
G = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] ;
A = e,
B = f,
G = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200].*/
So that the grouping is done with
groups(X,GS):-
findall( (A,B,G), bar(X,A,B,G), GS).
/* 68 ?- tuples(_X), groups(_X, GS).
GS = [(a, b, [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]),
(c, d, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]),
(e, f, [100, 200, 300, 400, 500, 600, 700, 800,
900, 1000, 1100, 1200])].
*/
update: from David Tonhofer's answer, we see that it can be done with just one nested bagof call,
solution( Sol ) :-
tuples(TS),
bagof( (A,B,FLS), % (* collect (A,B,FLS) for each (A,B) *)
LS^( % (* such that (A,B,L) is in TS *)
bagof( L, member((A,B,L), TS), LS ),
flatten( LS, FLS)
), % (* with the lists LS flattened *)
Sol ).
The outer bagof's goal predicate (i.e. the inner bagof call) is automatically backtracking to produce its LS result for each distinct pair of the A,B values, automagically achieving the same effect as we had "manually", in this answer.
group([],L,L).
group([(A,B,L)|T],Acc,Out):-
( append(L1,[(A,B,LL)|L2], Acc)
-> append(LL,L,LLL),
append(L1,[(A,B,LLL)|L2], NewAcc),
group(T,NewAcc,Out)
; append(Acc,[(A,B,L)],NewAcc),
group(T,NewAcc,Out)
).
?- tuples(L), group(L,[],G).
G = [
(a,b,[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]),
(c,d,[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]),
(e,f,[100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200])
],
L = [
(a,b,[1, 3, 5, 7]),
(a,b,[9, 11, 13, 15]),
(a,b,[17, 19, 21, 23]),
(c,d,[0, 2, 4, 6]),
(c,d,[8, 10, 12, 14]),
(c,d,[16, 18, 20, 22]),
(e,f,[100, 200, 300, 400]),
(e,f,[500, 600, 700, 800]),
(e,f,[900, 1000, 1100, 1200])
].
Basically I'm stripping an element from the (tuple) list ([(A,B,L)|T]) and look if I got a similar entry within my bag-list (2nd argument, append(L1,[(A,B,LL)|L2], Acc)). I use append/3 to do so because it can be used to find an element and at the same time dividing the original list into the list before and after the found element.
If I found an entry in the baglist, I append the two number lists (L and LL) to a new one (LLL) and exchange the values within the bag-list. Then I call the predicate again with the rest list and the altered bag-list.
If no element fits, I just add the element to the bag-list (as last element, append(Acc,[(A,B,L)],Acc2)) and call the predicate again. So each time I call the predicate group/3 it's first argument loses an element. Until there is no element left (group([],L,L).), in this case I state my bag-list is my ouput.
After playing a bit around with group_by/4 and/or bagof/3 as suggested by Guy coder, and using a variant of Davids "extraction" predicate here is another solution:
one_tuple(V,W,Value) :-
tuples(Tuples),
member((V,W,Values), Tuples),
member(Value, Values).
?- findall((A,B,Cs), bagof(C, one_tuple(A, B, C), Cs), O).
O = [(a,b,[1,3,5,7,9,11,13,15,17,19,21,23]),
(c,d,[0,2,4,6,8,10,12,14,16,18,20,22]),
(e,f,[100,200,300,400,500,600,700,800,900,1000,1100,1200])].
I want a function which takes the product of the inits of a list and duplicates its elements.
For example the list is: [2, 3, 4, 5].
The product of its inits : [1, 2, 6, 24, 120].
At the end the list should look like this: [1, 1, 2, 2, 2, 6, 6, 6, 6, 24, 24, 24, 24, 24].
My problem is that the [1, 2, 6, 24, 120] should not vary, but i can't solve it, I'm pretty new to haskell. You don't need to modify this code, you can make a new one.
makeSystem :: Integral a => [a] -> [a]
makeSystem l= replicate (l !! 0) ((map product(inits l))!!0) ++ asd (tail l) where
inits [] = [[]]
inits (x:xs) = [[]] ++ map (x:) (inits xs)
An other example: makeSystem [5,2,5,2,5,2]
The result: [1, 1, 1, 1, 1, 5, 5, 10, 10, 10, 10, 10, 50, 50, 100, 100, 100, 100, 100, 500, 500]
For the first part, you can use the standard function scanl:
> scanl (*) 1 [2, 3, 4, 5]
[1,2,6,24,120]
For the second part, zipWith with replicate gets us most of the way there:
> zipWith replicate [2, 3, 4, 5] [1, 2, 6, 24, 120]
[[1,1],[2,2,2],[6,6,6,6],[24,24,24,24,24]]
then we just need to concat these lists.
Putting it all together:
> let makeSystem xs = concat $ zipWith replicate xs (scanl (*) 1 xs)
> makeSystem [2, 3, 4, 5]
[1,1,2,2,2,6,6,6,6,24,24,24,24,24]
> makeSystem [5, 2, 5, 2, 5, 2]
[1,1,1,1,1,5,5,10,10,10,10,10,50,50,100,100,100,100,100,500,500]