我正在尝试创建一个在给定列表中的每个条目之前添加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)
答案 0 :(得分:6)
好像错误说的那样,您使用ins1
,然后编写[1] ++ [x] ++ ...
。
现在1
是一个数字文字,所以它可以采用所有数字类型。因此1
的类型为Num b => b
,因此[1]
的类型为Num b => [b]
。
稍后您使用x
和递归附加列表,因此我们现在知道a ~ b
(a
和b
属于同一类型)。因此,我们必须为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
= Int
到Num Int => Int
。这是有效的,因为有一个instance Num Int
。
但是,如果我选择a
为Char
,则为您提供['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
。