views:

102

answers:

1

I have a function (exercise 10.11 in Thompson's The Craft of Functional Programming) which computes an approximation to the value of the definite integral of a function over a domain (a, b). It might not be the most elegant function, but I'm still a a beginner:

import Data.Ratio (Rational, (%), denominator, numerator)
type R = Rational

integrate :: (R -> R) -> R -> (R, R) -> R       
integrate f d (a, b) = foldr (+) 0 $ zipWith (*) (map f [a, a + d..b]) (widths d)
 where widths :: R -> [R]
       widths = \n -> n : widths n

eval :: R -> Double       
eval = \r -> (/) (fromIntegral $ numerator r) (fromIntegral $ denominator r)

For instance,

eval $ integrate (\x -> 20 + x^2) (1%10000) (-3%1, 3%1) = 
 ~> 138.00290001

Now, widths d should be equivalent to the expression [d..]. However, if I replace widths by [d..] in integrate, my function outputs incorrect values. For instance:

integrate' :: (R -> R) -> R -> (R, R) -> R       
integrate' f d (a, b) = foldr (+) 0 $ zipWith (*) (map f [a, a+d..b]) [d..]

eval $ integrate' (\x -> 20 + x^2) (1%10000) (-3%1, 3%1)
 ~> 41400870141.0029

Why is this?

+3  A: 

Because the two statements aren't equivalent. Consider what happens when I call widths d:

widths d = d : widths d
         = d : d : widths d
         ...
         = [d, d, d, ...]

In other words, you get an infinite list of ds. However, [d..] returns the list [d, d+1, d+2, ...]. To get an infinite list of ds, you can write [d,d..]; in general, [d,d+n..] creates the infinite list [d, d+n, d+2*n, ...]. More idiomatically, one would generally write repeat d; repeat has the signature a -> [a], and just repeats its argument infinitely.

Edit: Also, some style, etc., points: function = \x -> ... is the same as function x = ... in all cases. And there's no particular reason to write your eval function with a prefix /; I'd write it eval r = (fromIntegral $ numerator r) / (fromIntegral $ denominator r); in actual fact, however, I'd just use the fromRational :: Fractional a => Rational -> a function instead of eval. You can also replace foldr (+) 0 with sum. And you don't need to create an infinite list of ds and then multiply everything; more simply, you could just have sum . map (* d) $ map f [a, a + d..b]. Of course, you can then distribute this out, and have

integrate'' :: (R -> R) -> R -> (R,R) -> R
integrate'' f d (a,b) = d * (sum $ map f [a, a+d .. b])`

And then we have

> fromRational $ integrate'' (\x -> 20 + x^2) (1%10000) (-3%1, 3%1) 
138.00290001
Antal S-Z
Thank you. Now I feel like a complete idiot.
danportin
@danportin: check out the tool hlint (for style suggestions and catching these kinds of things) and the search engine hoogle (for finding builtin functions with a certain type signature)
jberryman
Thanks for the style tips. I know that I could replace 'foldr (+) 0]' with sum, and lambda expressions by explicit arguments in the function definitions. However, I just learned about them; so I have been using them.However, you're right that 'map (*d) (map f [a, a+d..b' is cleaner and more efficient than what I wrote. Thanks for pointing out that I don't need to generate an infinite list of widths.
danportin