Equivalent expressions produce different results in a function?
I have a function (exercise 10.11 in Thompson's The Craft of Functional Programming) which computes an approximation to the value of the defi开发者_如何学运维nite 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?
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 d
s. However, [d..]
returns the list [d, d+1, d+2, ...]
. To get an infinite list of d
s, 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 d
s 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
精彩评论