Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compute Harmonic function lazily

Tags:

haskell

I am reading a Haskell book in which one of the exercises ask to compute the Harmonic function lazily, I've came with this solution, which I think it is lazy:

harmonic n = sum l
  where l = take n (map (1/) [1..])

But the answer on the book seems much more complicated:

harmonic n = sum (take n seriesValues)
  where seriesPairs = zip (cycle [1.0])  [1.0,2.0 .. ]
        seriesValues = map
                       (\pair -> (fst pair)/(snd pair))
                       seriesPairs

Which lead me to believe that my approach is in fact not lazy, but how so? I am handling an infinite list there.

like image 287
Alejandro Alcalde Avatar asked Apr 28 '20 06:04

Alejandro Alcalde


1 Answers

I mean you could benchmark it, but I don't have high hopes. The book's phrasing just looks awkward. It's screaming at me to do some equational refactoring

seriesPairs = zip (cycle [1.0]) [1.0,2.0 .. ]
seriesPairs = zip (cycle [1]) [1,2..]  -- trust type inference
seriesPairs = map (1,) [1,2..]         -- unnecessary zip of repeat list


seriesValues = map (\pair -> (fst pair)/(snd pair)) seriesPairs
seriesValues = map (\(a,b) -> a/b) seriesPairs  -- pattern matching!

seriesValues = map (\(a,b) -> a/b) (map (1,) [1,2..])
seriesValues = map ((\(a,b) -> a/b) . (1,)) [1,2..]   -- map fusion
seriesValues = map (\b -> 1/b) [1,2..]
seriesValues = map (1/) [1..]


harmonic n = sum (take n seriesValues)
harmonic n = sum (take n (map (1/) [1..]))

Look familiar? I believe all these transformations are available to the optimizer so if you compile with optimizations I would expect them to perform the same. Don't hold me to that though. But they are the same program, denotationally speaking. Looks to me like the author just isn't very fluent in Haskell (especially that (\pair -> (fst pair)/(snd pair)) line has quite a thick accent).

I wouldn't call either of these functions lazy. In fact, if it takes an Int argument and a Double result, the only function I would call "lazy" is the constant function.

Here's something I would call lazy: computing an infinite list of harmonic numbers, re-using intermediate results.

harmonicNumbers :: [Double]
harmonicNumbers = scanl (+) 0 (map (1/) [1..])
like image 109
luqui Avatar answered Sep 28 '22 16:09

luqui