Haskell错误 - 文字“1”中没有(Num a)的实例

时间:2018-06-17 10:05:22

标签: list haskell

我正在尝试创建一个在给定列表中的每个条目之前添加1的函数。我还没有完全掌握Haskell的语法,我想知道这段代码有什么问题。例如,我希望这返回列表[1,1,1,2,1,3]

ins1 :: [a] -> [a]
ins1 [x] = [x]
ins1 (x:xs) = [1] ++ [x] ++ ins1(xs)

main = print(ins1 [1,2,3])

我收到错误:

• No instance for (Num a) arising from the literal ‘1’
  Possible fix:
    add (Num a) to the context of
      the type signature for:
        ins1 :: [a] -> [a]
• In the expression: 1
  In the first argument of ‘(++)’, namely ‘[1]’
  In the expression: [1] ++ [x] ++ ins1 (xs)
<interactive>:3:1: error:
• Variable not in scope: main
• Perhaps you meant ‘min’ (imported from Prelude)

2 个答案:

答案 0 :(得分:6)

好像错误说的那样,您使用ins1,然后编写[1] ++ [x] ++ ...

现在1是一个数字文字,所以它可以采用所有数字类型。因此1的类型为Num b => b,因此[1]的类型为Num b => [b]

稍后您使用x和递归附加列表,因此我们现在知道a ~ bab属于同一类型)。因此,我们必须为a

的签名添加类型约束
ins1 :: Num a => [a] -> [a]
ins1 [x] = [x]
ins1 (x:xs) = [1] ++ [x] ++ ins1(xs)

这解决了编译错误,但可能生成你想要的东西。从现在起,空列表中存在 no 大小写。实际上,[x]模式和(x:xs)模式都可以使用分别与一个元素的列表匹配的列表,并且至少一个元素

因此我认为您的第一个子句实际上应与空列表匹配:

ins1 :: Num a => [a] -> [a]
ins1 [] = []
ins1 (x:xs) = [1] ++ [x] ++ ins1(xs)

第二个子句中效率低下:你追加到一个元素的列表,所以我们可以在这里使用“ cons ”数据cosntructor (:)

ins1 :: Num a => [a] -> [a]
ins1 [] = []
ins1 (x:xs) = 1 : x : ins1 xs

这将在原始列表中为每个元素插入一个1,所以:

Prelude> ins1 [1, 4, 2, 5]
[1,1,1,4,1,2,1,5]

答案 1 :(得分:1)

如果你给我一个[a] -> [a]类型的函数,你就是说用于选择的所有类型a,如果我给你一个类型a的值列表,然后你可以给我一个这种类型的元素列表。

因此,如果我通过向您a选择Int[2, 3, 4] :: [Int],那么一切都很顺利:1的实施中的文字ins1是约Num t => t t = IntNum Int => Int。这是有效的,因为有一个instance Num Int

但是,如果我选择aChar,则为您提供['a', 'b', 'c'](= "abc"),然后t = Char ,给出Num Char => Char,这是一个错误,因为没有instance Num Char。因此,对于类型,这是反例:您的函数不适用于所有 a,只有那些具有实例的a Num。所以你需要在类型签名中表达这个约束:

ins1 :: (Num a) => [a] -> [a]

编译器可以为您推断出这一点,并且如果启用-Wall(或具体为-Wmissing-signatures),则会显示有关缺少类型签名的警告。这也会警告ins1 非详尽的事实:您不处理空输入列表的情况。或者,您可以在GHCi中输入定义,并使用:type ins1:t ins1询问其类型。

Haskell中的通用函数是参数化多态,这意味着如果你有一个不受约束的类型变量,如a,你就知道 nothing ,甚至没有它可以用数字文字构成。因此,类型[a] -> [a]的函数可以执行的事物是从输入复制,重新排列或删除元素,或者无法终止(无限循环或引发错误) - 可以'构造新元素或对这些元素执行任何特定于类的操作,例如来自+的{​​{1}}或来自Num的{​​{1}}。

这可能听起来有限,但事实上它非常有用:你对类型的了解越少,滥用的选项就越少。通过一个称为Curry-Howard对应关系的巧妙技巧,您可以检查像<这样的多态函数类型,并将其视为一个逻辑公式:确实有一个Ord列表暗示您可以获得{ {1}}?不,因为列表可能是空的。因此,如果输入为空,则知道具有此类型head :: [a] -> a函数必须引发错误,因为它没有通用的方法来构造a