"Mark Engelberg" <[EMAIL PROTECTED]> wrote,

> After reading about the stellar performance of the Haskell entries in the
> ICFP contest, I started reading the Haskell documentation to learn more
> about the language.  My knowledge of functional programming languages
> primarily comes from Scheme.
[...] 
> This seems like a perfect opportunity to exploit Haskell's list generation
> and lazy evaluation capabilities.  The crux of the problem is to find a
> concise way of describing the list of all permutations of digits.  I don't
> want to embarrass myself by posting my first attempt here, but I'll show you
> my second, slightly-less naive approach to writing this program:
> 
[Haskell program deleted]
>
> Impressively concise, but how does it run?  Well, I loaded it into the HUGS
> interpreter; typed answer, and after a couple minutes, the answer printed
> out.  Not too bad, but how would this compare to a
> solution in C++?
>
[C++ program deleted]
> 
> It's a little longer, but not unreasonably so.  How does it run?  Well,
> running it inside the debugger, it generated the correct answer within a
> couple of seconds.
> 
> This is a huge difference, and makes Haskell look incredibly slow by
> comparison.  The speed difference in this case is well worth the extra few
> lines of code.

After modifying you program a bit:

  import List

  expand a b c d e f = f + e*10 + d*100 + c*1000 + b*10000 + a*100000

  xs :: [Int]
  xs = [0,1..9]  -- all the digits

  answer =
    head [ [t,h,i,r,y,w,e,l,v,n] 
         | t<-xs, 
           let es = delete t xs, e<-es, 
           let hs = delete e es, h<-hs,
           let is = delete h hs, i<-is,
           let rs = delete i is, r<-rs,
           let ys = delete r rs, y<-ys,
           let ws = delete y ys, w<-ws,
           let ls = delete w ws, l<-ls,
           let vs = delete l ls, v<-vs,
           let ns = delete v vs, n<-ns,
           expand t h i r t y + 5 * expand t w e l v e == expand n i n e t y]

I get

  C++ (your program)  : 0.31s
  Haskell (above prgm): 2.63s
  (This was on a 300MHz Celeron running Linux.  C++ compiled 
  with egcs-1.1.2 and -O3.)

Still slower, but not too bad (given that C++ probably uses
inplace array manipulations).  So, why the difference to
your results:

* I used a Haskell _compiler_, not the Hugs interpreter (I
  used GHC 4.04pl1 and compiled with -O2).

* I added a type annotation to the definition of `xs' -
  Haskell uses overloading on numerals and in your case
  conservatively assumed that you want to calculate with
  _arbitrary_precision_ integer arithmetic.

* I moved redundant computations out of the inner `loops'.
  The farther a generator to the right, the further inside a
  set of nested `loops' (actually recursions) it is.  For
  example, your `n<-xs\\[t,e,h,i,r,y,w,l,v]' generator
  strips `t', `e', `h', `i', `r', `y', `w', and `l' from the
  list although this was already done in the enclosing
  generator (the one to its left).

If you get to know Haskell a little, the above are fairly
straight forward optimisations (ie, nothing surprising).

Cheers,

Manuel


Reply via email to