【问题标题】:Haskell. Type error when loading function but not inline哈斯克尔。加载函数时输入错误但不是内联
【发布时间】:2018-05-10 02:53:57
【问题描述】:

我是 Haskell 的新手。我把它输入到 WinGHCi 中,它工作正常:

> let x = 0.5
> let n = 5
> map (\y->(x**y)) [0..n]
[1.0,0.5,0.25,0.125,6.25e-2,3.125e-2]  -- notice it is powers of 1/2 !

但是当我在文件中定义一个简单的函数时:

powersOfx :: (Integral a, Floating b) =>  a -> b -> [b]
powersOfx n x = map (\y->(x**y)) [0..n]

然后输入 :l myFile,我明白了:

 Couldn't match expected type ‘b’ with actual type ‘a’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          powersOfx :: forall a b. (Integral a, Floating b) => a -> b -> [b]

发生了什么事?我做的签名不正确吗?我猜我可能是,因为当我把它注释掉并且 :l myFile 它工作但

:t powersOfx 

我明白了:

powersOfx :: (Floating b, Enum b) => b -> b -> [b]

注意“枚举”而不是“积分”。

我想我可以摆脱类型签名,但我的印象是放置签名是一种很好的做法,我正在尝试解决一个更大的问题,在这里我收到错误报告: Ambiguous type variable `a0' arising from a use of `it'

如果我让这部分工作,我将发布一个单独的问题!

请让我知道我是否会更好地发布到另一个组,或者我是否应该发布更多信息。

-戴夫

【问题讨论】:

  • 它必须是Enum,否则你不能进行[0..n]的操作。至于Floating——** 的操作数必须是同一类型。也许您正在寻找^ 运算符?另外,powersOf x = map (x^) [0..] 你也可以take 5 (powersOf 0.5)

标签: haskell


【解决方案1】:

map (\y->(x**y)) [0..n] 函数的签名

简而言之:你的签名太宽泛了,可以设计Floating类型使得函数不能被调用。

让我们先分析一下你这里使用的类型:

Prelude> let x = 0.5
Prelude> let n = 5
Prelude> :t map (\y->(x**y)) [0..n]
map (\y->(x**y)) [0..n] :: (Enum b, Floating b) => [b]
Prelude> :t \n x -> map (\y->(x**y)) [0..n]
\n x -> map (\y->(x**y)) [0..n]
  :: (Enum b, Floating b) => b -> b -> [b]

所以我们在这里看到的是输出类型b,实际上必须是两个类型类的实例:EnumFloating。这是合乎逻辑的:您使用[0..n],这意味着您要求Haskell 枚举0n 之间的所有元素,但是一个数字本身并不是可枚举的(实际上我们已经想知道Floating 是首先是Enumerable:这里我们跳了一个,但是我们因此省略了两者之间的Floating值,以防我们有一个真正的浮点数,那么枚举甚至是不可能的)。

因此ys 与n 具有相同的类型,并且由于我们使用xy(我们写x ** y)执行算术运算,因此这意味着x 和@ 987654344@ 需要具有相同的类型,因为 (**) 具有类型 (**) :: Floating a => a -> a -> a。因此我们可以构造一个函数:

powersOfx :: (Enum b, Floating b) => b -> b -> [b]
powersOfx n x = map (\y->(x**y)) [0..n]

但这是个坏主意。 Floating 通常是我们最好尽量避免的:如果数量很大,我们可能会有各种舍入错误。我们可以使用fromIntegral :: (Integral a, Num b) => a -> b,但是当我们将大的Int 转换为Float 时,这又会导致舍入错误。在这种情况下,类型将是:

powersOfx :: (Enum b, Floating b, Integral a) => a -> b -> [b]
powersOfx n x = map (\y->(x**y)) [0..fromIntegral n]

制作更通用的变体

不过,我们可以通过使用iterate :: (a -> a) -> a -> [a]take :: Int -> [a] -> [a](或genericTake :: Integral i => i -> [a] -> [a])使函数更加灵活和通用:

import Data.List(genericTake)

powersOfx :: (Integral i, Num n) => i -> n -> [n]
powersOfx n x = genericTake (n+1) (iterate (x*) 1)

这会产生:

Prelude Data.List> f 5 0.5
[1.0,0.5,0.25,0.125,6.25e-2,3.125e-2]

【讨论】:

  • 谢谢威廉。两个厘米。 1)当我尝试您的示例时,我收到错误错误:输入“导入”时出现解析错误。 2)更大的问题。我很欣赏您的示例,但是如何使我的原始方法起作用?正如我所说,当您直接将其键入 WinGHCi 时它工作正常,但会产生用“:load”描述的错误。我尝试将签名更改为 powersOfx :: (Enum a, Floating b) => a -> b -> [b] 并得到与使用 Integral 时相同的错误。
  • (1) 这可能是因为您在 import 语句之前定义了函数。 import 语句应分组在顶部 (2) 通过将签名更改为上面的一个;但这会使函数变得很糟糕,因为那时n 被认为是Floating,并且这些通常可能有舍入错误。您可以开始在Integrals 和fromIntegral 之间进行转换,但这又会导致舍入错误。签名是:: (Enum b, Floating b) => b -> b -> [b](注意这里只有b,不是a)。
  • 非常感谢威勒姆!越来越清楚了。我重读了你的答案,并且(希望是正确的)推断出你的答案意味着什么。 1)是的,我把 import ant 放在文件的开头,一切都很好。 2)一旦我将所有'b'而不是'a'和'b'的某种组合放在 :: (Enum b, Floating b) => b -> b -> [b] 中,所有工作都有效。我假设必须有一个“a”和一个“b”,因为有两个参数,n 和 x。但是 Enum b, Floating b 是指两个参数都需要浮动和枚举的事实。我明白为什么两者都必须浮动(因为我使用了**),但不明白为什么两者都需要枚举。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多