logo
Published on KernelTrap (http://kerneltrap.org)

Ice Cream Problem

By Greg Buchholz
Created Jan 24 2006 - 19:12

Brian Harvey throws down the gauntlet: implement the Ice Cream [1] problem [2] 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;
}

Source URL:
http://kerneltrap.org/node/6101