Haskell错误 - 没有由文字"1"引起的(Num a)实例



我正在尝试创建一个函数,该函数在给定列表中的每个条目之前添加一个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)

就像错误所说的那样,你使用ins1,你写[1] ++ [x] ++ ....

现在1是一个数字文字,所以它可以接受所有数值类型。因此1具有类型Num b => b,因此[1]具有类型Num b => [b]

稍后你用x和递归附加列表,因此我们现在知道a ~ b(ab是相同的类型(。所以我们必须在签名中添加一个类型约束a

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

这解决了编译错误,但可能不会生成您想要的内容。从现在开始,没有空列表的情况。实际上,[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]

如果你给我一个类型[a] -> [a]的函数,你是说,对于选择的所有类型a,如果我给你一个a类型的值列表,那么你可以给我一个该类型的元素列表。

因此,如果我选择a通过为您提供[2, 3, 4] :: [Int]Int,那么一切都很好:ins1实现中的文字1受到t=IntNum Int => Int的约束Num t => t。这是有效的,因为有一个instance Num Int.

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

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

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

Haskell中的泛型函数是参数多态的,这意味着如果你有一个像a这样的不受约束的类型变量,你对它一无所知,甚至不知道它可以由数字文字构造。因此,类型[a] -> [a]函数唯一能做的就是从输入中复制、重新排列或删除元素,或者无法终止(无限循环或引发错误(——它不能构造新元素或对这些元素执行任何特定于类的操作,例如从Num+或从Ord<

这听起来可能很有限,但实际上它非常有用:您对一种类型的了解越少,滥用它的选择就越少。通过一个叫做库里-霍华德对应关系的巧妙技巧,你可以检查像head :: [a] -> a这样的多态函数类型,并将其视为一个逻辑公式:有一个a列表是否意味着你可以得到一个a?否,因为列表可能为空。所以你知道,如果输入为空,这种类型的head函数必须引发错误,因为它没有通用方法来构造a

最新更新