Brian Harvey throws down the gauntlet: implement the Ice Cream problem in a small number of lines of Java. Well, since I don't know Java, I'll take up the challenge in C++...
template<template<class> class Out, class Obj, template<class> class G, template<class> class F>
Out<Obj> comb(typename G<F<Obj> >::const_iterator b, typename G<F<Obj> >::const_iterator e)
{
typename G<F<Obj> >::const_iterator tail = b;
if(++tail == e) { return *b; }
else
{ // recursivly concatenate the items in the head of the list
// with the tails...
G<F<Obj> > temp;
transform((*b).begin(),(*b).end(),back_inserter(temp),bind1st(ptr_fun(aux<Out,Obj>),comb<Out,Obj,G,F>(tail,e)));
//now flatten temp...
Out<Obj> out;
for(typename G<F<Obj> >::const_iterator i = temp.begin(); i != temp.end(); ++i)
copy((*i).begin(),(*i).end(),back_inserter(out));
return out;
}
}
template<template<class> class Container, class Obj>
Container<Obj> aux(Container<Obj> ps, Obj n)
{
Container<Obj> out;
transform(ps.begin(),ps.end(),back_inserter(out),bind1st(plus<string>(),n));
return out;
}
...OK. That's pretty verbose. But it is interesting to note that there are only ~5 lines of code doing the work there. The rest is dedicated to declaring stuff like types. The other thing to notice is that the C++ solution above is pretty generic. You can feed it a list of list of strings, or a vector of deque's of strings, or even a list of stacks of valarray<double>'s (don't ask me what you'd use that for). You can save a few LOC and a little bit of uglyness by specializing it for lists of lists of strings. Of course, that still pales in comparison to the two line Haskell solution...
combs [a] = a
combs (x:xs) = concat $ map (\n -> map (n++) (combs xs)) x
...and for interested parties, here is a driver program, so you can get up to speed quickly (and catch any of my mistakes)...
#include <list>
#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
template<template<class> class Out, class Obj,
template<class> class G, template<class> class F>
Out<Obj> comb(typename G<F<Obj> >::const_iterator b,
typename G<F<Obj> >::const_iterator e);
template<template<class> class Container, class Obj>
Container<Obj> aux(Container<Obj> ps, Obj n);
int main()
{
char *c[] = {"small ","medium ","large ",
"vanilla ","ultra chocolate ","lychee ","rum raisin ","ginger ",
"cone","cup"};
int len[] = { 3, 5, 2 };
list<list<string> > choices;
for(int i=0, off=0; i<3; off += len[i++])
{
list<string> temp(c+off,c+off+len[i]);
choices.push_back(temp);
}
list<string> o = comb<list,string,list,list>(choices.begin(),choices.end());
copy(o.begin(),o.end(),ostream_iterator<string>(cout, ".\n"));
return 0;
}
Here's a version in C. It's d
Here's a version in C. It's different in that it produces the results in a different order, it doesn't use recursion, and it doesn't construct temporary lists. Instead, it uses an encoding for a choice.
Oh, it uses C style array initialization.
---
static char *sub0[] = {"small", "medium", "large", 0}, *sub1[] = {"vanilla", "ultra chocolate", "lychee", "rum raisin", "ginger", 0}, *sub2[] = {"cone", "cup", 0}, **menu[] = {sub0, sub1, sub2, 0}; static int lgth(char *sub[]) { int n = 0; while (*sub++) n++; return n; } static int poss(char **menu[]) { int n = 1; while (*menu) n *= lgth(*menu++); return n; } static int choices(char **menu[]) { int n = poss(menu); while (0<=--n) { char ***m = menu; int i = n; while (*m) { char **s = *m++; int l = lgth(s); printf(" %s",s[i%l]); i /= l; } printf("\n"); } return n; } int main(int argc, char *argv[]) { choices(menu); return 0; }I should add that this is rea
I should add that this is really an APL function coded in C
Nice. Out of curiosity, what
Nice. Out of curiosity, what would the APL (or A+, J, or K) version look like?
I don't have a working APL ri
I don't have a working APL right now, so I can't really try this -
The idea is to compute the number of possible choices, then iterate.
The iteration count represents a choice which decodes to a set of indices, one for the selection in each submenu. APL has a decode operator for this sort of thing.
In Ocaml...
Here it is in OCaml (http://caml.inria.fr/):
The compiler returns the type
which is a clear indication that it has the same polymorphism as your C++ version ('a stands for an arbitrary type). You can use it this way:
comb [["small"; "medium"; "large"]; ["vanilla"; "ultra chocolate"; "rum raisin"; "ginger"]; ["cone"; "cup"]](BTW, I do not think the base case in your Haskell version is correct -- and the case [] is missing.)
Not quite
The C++ is more polymorphic. The Ocaml only works with lists of lists. The C++ closer to something like Haskell's
Monad m => m (m a) -> m (m a), but it'll even work with arrays and things that aren't monads.Shorter Haskell
combs = map unwords . sequence
It also saves putting spaces in all the strings.
The sequence function is the magic, in the list monad it generates all the combinations.
bash wins again
#!/bin/sh for a in small medium large; do for b in vanilla 'ultra chocolate' lychee 'rum raisin' ginger; do for c in cone cup; do echo $a $b $c done done doneA compact Java version
Here's a java implementation. It's not all that huge ...
import java.util.Arrays; import java.util.List; public class IceCream { private void build(String sentence, List... components) { if (components.length == 1) for(String component : components[0]) System.out.println(sentence + " " + component); else { List[] subComponents = new List[components.length - 1]; System.arraycopy(components, 1, subComponents, 0,components.length - 1 ); for(String component : components[0]) build(sentence + " " + component, subComponents); } } public static void main(String[] args) { new IceCream().build("", Arrays.asList("small","medium","large"), Arrays.asList("vanilla","ultra chocolate","lychee","rum raisin","ginger"), Arrays.asList("cone","cup")); } }Python FTW
size = ["Small ", "Medium ", "Large "]
flavour = ["Vanilla", "Ultra Chocolate", "Rum Raisin", "Ginger"]
container = [" Cone", " Cup"]
combs = [i + j + k for i in size for j in flavour for k in container]
for comb in combs:
print comb