【问题标题】:Equivalent expressions produce different results in a function?等效表达式在函数中产生不同的结果?
【发布时间】:2010-07-24 04:32:31
【问题描述】:

我有一个函数(Thompson 的函数式编程技巧中的练习 10.11),它计算一个函数在域 (a, b) 上的定积分值的近似值。它可能不是最优雅的功能,但我仍然是初学者:

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)

例如,

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

现在,widths d 应该等价于表达式 [d..]。但是,如果我在积分中用 [d..] 替换宽度,我的函数会输出不正确的值。例如:

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

这是为什么?

【问题讨论】:

  • 而不是“EDIT: Solved”,在这个网站上,礼仪是标记正确答案(在这种情况下,我的,因为它是唯一的,你认为问题已解决)通过单击复选标记“已接受”。
  • 您可以使用repeat d 而不是widths d

标签: haskell integration


【解决方案1】:

因为这两个语句不等价。想想当我打电话给widths d时会发生什么:

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

换句话说,您会得到一个无限的ds 列表。但是,[d..] 返回列表 [d, d+1, d+2, ...]。要获得ds 的无限列表,可以写[d,d..];通常,[d,d+n..] 创建无限列表[d, d+n, d+2*n, ...]。更惯用的说法是,通常会写成repeat d; repeat 有签名a -> [a],只是无限重复它的参数。

编辑:另外,一些风格等,点:function = \x -> ... 在所有情况下都与function x = ... 相同。并且没有特别的理由来编写带有前缀/eval 函数;我会写eval r = (fromIntegral $ numerator r) / (fromIntegral $ denominator r);然而,实际上,我只是使用fromRational :: Fractional a => Rational -> a 函数而不是eval。您也可以将foldr (+) 0 替换为sum。而且您不需要创建ds 的无限列表,然后将所有内容相乘;更简单地说,你可以只拥有sum . map (* d) $ map f [a, a + d..b]。当然,你可以把它分发出去,然后有

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

然后我们有

> fromRational $ integrate'' (\x -> 20 + x^2) (1%10000) (-3%1, 3%1) 
138.00290001

【讨论】:

  • @danportin:查看工具 hlint(用于样式建议和捕捉这些东西)和搜索引擎 hoogle(用于查找具有特定类型签名的内置函数)
  • 感谢您的风格提示。我知道我可以用 sum 替换 'foldr (+) 0]',并用函数定义中的显式参数替换 lambda 表达式。但是,我刚刚了解了它们;所以我一直在使用它们。但是,你说得对,'map (*d) (map f [a, a+d..b' 比我写的更干净、更高效。感谢您指出我不需要生成无限宽度列表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 2015-02-13
  • 1970-01-01
  • 2011-05-16
相关资源
最近更新 更多