【问题标题】:Understanding types in Haskell理解 Haskell 中的类型
【发布时间】:2014-03-17 02:33:05
【问题描述】:

我正在尝试将函数 h 映射到素数列表。两者都在下面给出:

f k x = floor ( log k / log x )

h = f 20

primes = 2 : sieve [3,5..]
  where
    sieve (p:xs) = p:[ x | x <- xs, x `mod` p > 0 ]

但是,当我尝试 map h primes 时,出现以下错误:

Ambiguous type variable `a0' in the constraints:
  (Enum a0)
    arising from the arithmetic sequence `3, 5 .. '

等等……

函数 f 和 primes 似乎都按预期工作,但我不能将 f 应用于素数中的数字???我在这里误会了什么?

【问题讨论】:

  • 啊,不要在回答后将问题编辑为新形式。你可以问一个新的。 :)
  • 啊,对不起。我应该恢复我的编辑并创建一个新问题吗?
  • 不要还原它。我已经更新了答案。

标签: haskell types


【解决方案1】:

您的函数primes 的类型为[Integer]

您的函数h 的类型为Double -&gt; Integer

map的类型是这样的:

ghci> :t map
map :: (a -> b) -> [a] -> [b]

或者当专门用于Integer 列表时,它的类型签名变为:

map :: (Integer -> b) -> [Integer] -> [b]

但是您传递给maph 函数是Double -&gt; Integer 类型,因此它不会进行类型检查,因为它期待Integer -&gt; b 而不是接受Double 的函数。

总是尝试在函数之前写类型签名,这会让你的生活更轻松。

【讨论】:

  • 对不起,我在发布问题时犯了一个错误。我实际上正在应用h = f 20 并收到上述错误。有更新的问题。
【解决方案2】:

问题

primes :: Integral a =&gt; [a]h :: (RealFrac a, Integral b, Floating a) =&gt; a -&gt; b。现在,没有任何类型同时是 RealFracIntegral 的实例,但 GHC 不知道这一点,所以它给出的错误消息有点令人困惑。

原因

log :: Floating a =&gt; a -&gt; a(/) :: Fractional a =&gt; a -&gt; a -&gt; afloor :: (RealFrac a, Integral b) =&gt; a -&gt; b,所以当我们组合它们时,我们会得到上面的类型签名。 Haskell 具有强大的数字类型,这意味着整数和浮点类型之间没有隐式转换。

解决方案

使用显式转换函数fromIntegral :: (Integral a, Num b) =&gt; a -&gt; b

map (h . fromIntegral) primes

【讨论】:

    【解决方案3】:

    primes 有什么类型?这完全取决于情况。可能是

    primes :: [Integer]
    

    或者别的什么。目前还不清楚,因为像3 这样的文字具有3 :: Num a =&gt; a 类型。我们唯一可以确定的是,无论它需要是Enum 的实例,否则[3,5..] 将无法工作。

    现在f 的类型是什么? f 在它的参数上使用 log,然后将结果放在地板上,所以我们可以期待类似的东西

    f :: (RealFrac a, Floating a, Integral b) => a -> a -> b
    

    但是,这已经暗示您不能将它与map 一起使用,因为map 需要(a -&gt; b) 作为第一个参数。更重要的是,primes 的元素不满足Floating 约束:

    *Main> let (p1:p2:_) = primes
    *Main> f p1 p2
    
    <interactive>:29:1:
        No instance for (RealFrac Integer) arising from a use of `f'
        Possible fix: add an instance declaration for (RealFrac Integer)
        In the expression: f p1 p2
        In an equation for `it': it = f p1 p2
    

    所以我们要么需要更改primes 的类型,要么需要更改f 的类型。我们改变f的类型:

    f k x = floor ( log (fromIntegral k) / log (fromIntegral x) )
    

    现在我们可以按预期使用f p1 p2

    *Main> let (p1:p2:_) = primes
    *Main> f p1 p2
    0
    

    但是map 问题呢? map 期望 (a -&gt; b) 作为第一个参数,而 f 仍然是 (a -&gt; b -&gt; c)*。从您当前的代码来看,您似乎想使用两个连续的素数并应用f。为此,我们首先使用uncurry

    uncurry :: (a -> b -> c) -> (a, b) -> c
    

    现在uncurry f 拥有(a, b) -&gt; c。但现在列表 (primes) 不是对列表。但是,我们可以很容易地解决这个问题,我们 zip 用它的尾巴素数:

    map (uncurry f) (zip primes $ tail primes)
    

    这就是您如何将具有两个参数的函数映射到单个列表的连续元素上。


    *k 或 x 可以是不同的 Integral 类型

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-28
      • 2014-05-04
      • 2017-01-04
      • 1970-01-01
      • 1970-01-01
      • 2021-08-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多