SICP练习中的Haskell数字类型层次结构

5

我最近在学习Haskell,和正在学习SICP的朋友聊天时,我们想比较一下Common Lisp和Scheme,所以我决定尝试将练习1.29翻译成Haskell作为练习。

此练习使用表示数学求和函数Sigma的函数sigma。该函数接受一个函数f,用于应用于每个项,一个下限,一个用于获取下一个项的函数,并且一个上限。它返回应用于每个项的f的总和。

simpsonIntegral应该使用Simpson法则来使用“精度”n近似计算函数f在范围[a,b]上的积分。 我无法让这个函数正常工作,因为似乎有些关于涉及类型的东西我不明白。

这段代码将在ghc 6.12.1版本中编译,但是simpsonIntegral将会被赋予(Integral a, Fractional a)这样一个没有任何意义的类型上下文,而且一旦调用它,该函数就会失效。 我曾经让它工作过,但我所做的显然是一个Hack,我想问问在这里应该如何惯用地处理它。

应该如何惯用地处理h中需要的整数 -> 分数/实数转换? 我读了很多东西,但没有什么是明显和干净的。

sigma :: (Ord a, Num b) => (a -> b) -> a -> (a -> a) -> a -> b
sigma f a next b = iter a 0
  where
    iter current acc | current > b = acc
                     | otherwise = iter (next current) (acc + f current)

simpsonIntegral f a b n = 1.0 * (h / 3) * (sigma simTerm 0 (1+) n)
  where
    h = (b - a) / n
    simTerm k = (yk k) * term
      where
        yk k = f (a + h * k)
        term =
          case k of
            0 -> 1
            1 -> 1
            otherwise -> if odd k then 4 else 2
3个回答

6
fromIntegral :: (Integral a, Num b) => a -> b

r = fromIntegral i

关于 fromIntegral 的问题。我尝试了这个,但根据 Travis 的回答,我没有在正确的地方使用它,而且我认为它不起作用是因为根据这个网址 http://www.haskell.org/tutorial/numbers.html,num 不提供除法运算符。fromIntegral 在返回类型上是多态的吗?ghc 推断我想要一个 Fractional 返回类型吗? - asm
是的,fromIntegral 在其返回类型 b 中具有多态性,即允许 b(返回类型)为 Num 类型类的成员的任何数据类型。虽然 Num 类型类不提供除法,但是一些作为该类型类成员的数据类型确实提供了除法,例如 Double - yfeldblum

3

跟进Justice的回答:如果你好奇fromIntegral应该放在哪里,以下代码可以编译通过:

simpsonIntegral :: (Integral a, Fractional b) => (b -> b) -> a -> a -> a -> b
simpsonIntegral f a b n = 1.0 * (h / 3) * (sigma simTerm 0 (1+) n)
  where
    h = fromIntegral (b - a) / fromIntegral n
    simTerm k = (yk k) * term
      where
        yk k = f (fromIntegral a + h * fromIntegral k)
        term = 
          case k of
            0 -> 1
            1 -> 1
            otherwise -> if odd k then 4 else 2

看起来似乎可行:

*Main> simpsonIntegral (^3) 0 1 100
0.2533333233333334
*Main> simpsonIntegral (^3) 0 1 1000
0.2503333333323334

1
问题在于函数“odd”期望它的参数是一个整数类型。编译器随后推断出您的变量“k”是Integral类型。但是通过使用操作符“/”,编译器推断出“k”也是Fractional类型。解决方案可以简单地将“k”转换为整数类型,以确保其真正需要的地方:
if odd (round k) then 4 else 2

如果你想了解更多有关 Haskell 中数字转换的知识,请查看数字转换

另外,这里还有一种编写 sigma 函数的方式:

sigma f a next b = sum $ map f $ takeWhile (<= b) $ iterate next a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接