Haskell 在 GHCi 中的 head tail init 和 last
Haskell's head tail init and last in GHCi
我有一个关于 head
、tail
、init
和 last
的问题。
GHCi 中的以下作品:
Prelude Data.List Data.Char> let n = [1..10] in (head n : tail n)
[1,2,3,4,5,6,7,8,9,10]
不出所料,我得到了整个列表。所以这也适用于 init
和 last
,
对吗?
Prelude Data.List Data.Char> let n = [1..10] in (init n : last n)
<interactive>:39:1:
Non type-variable argument in the constraint: Enum [[a]]
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a. (Enum a, Enum [[a]], Num a, Num [[a]]) => [[a]]
如果我查看函数的类型签名,那么 head
和 last
看起来一样——它们都是 return 一个元素。还有 init
和 tail
看看
相同,因为它们都 return lists.
Prelude Data.List Data.Char> :info head
head :: [a] -> a -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info tail
tail :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info init
init :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info last
last :: [a] -> a -- Defined in ‘GHC.List’
那么Non type-variable argument in the constraint: Enum [[a]]
是什么意思呢?
如果我在没有构建新列表的情况下执行 init n
或 last n
,我会得到
[1..9]
和 10
.
啊啊啊啊,:
像 x:xs
一样接受一个元素和其余元素,而不是像 xs:x
中的列表和最后一个元素。
Prelude Data.List Data.Char> :info :
data [] a = ... | a : [a] >·-- Defined in ‘GHC.Types’
infixr 5 :
data [] a = ... | a : [a] >·-- Defined in ‘GHC.Types’
infixr 5 :
我仍然想知道你是如何理解 GHCi 错误消息的,无论如何我必须等待 2 天才能接受我自己的答案。
编辑:我知道 x
和 xs
只是约定俗成的变量名,(xs:_)
会匹配头部但被命名为 unconventionally/confusingly。
编辑 2:我赞成并接受了 Daniel Wagner 的回答,因为他逐步解释了错误消息。非常好!谢谢!
确实 head
/last
和 tail
/init
具有相同的类型。因此,如果您只是将 last
换成 head
,将 init
换成 tail
,您就不会有问题:
> let n = [1..10] in last n : init n
[10,1,2,3,4,5,6,7,8,9]
但你没有。您同时交换了 和另一个 :您将参数的顺序更改为 :
。碰巧 :
不接受两个相同类型的参数:
> :t (:)
(:) :: a -> [a] -> [a]
所以这最后一次交换是不对的!事实上,如果你给 n
一个稍微更具体的类型签名,ghci 会给出更好的错误:
> let n :: [Integer]; n = [1..10] in init n : last n
<interactive>:1:50:
Couldn't match type ‘Integer’ with ‘[[Integer]]’
Expected type: [[[Integer]]]
Actual type: [Integer]
In the first argument of ‘last’, namely ‘n’
In the second argument of ‘(:)’, namely ‘last n’
这个错误仍然不是 100% 清楚,但我认为有点令人费解的是你可以看出它在抱怨什么:因为 init n :: [Integer]
和 (:) :: [Integer] -> [[Integer]] -> [[Integer]]
,它期待 last n :: [[Integer]]
因此 n :: [[[Integer]]]
。但是你明明说了n :: [Integer]
,冲突了
现在,在您的案例中它实际给您的错误是什么?好吧,线索在 [1..10]
:
的类型中
> :t [1..10]
[1..10] :: (Enum t, Num t) => [t]
注意 [1..10]
是多态的。此外,它在您的表达式中使用了两次,因此可以在两次使用中给它 separate 单态类型。所以 [1..10]
在 sequel!
中用 两个 不同类型实例化
现在我想您可以开始查看您遇到的错误是从哪里来的。它试图找到一个类型 a
用于:
Enum a
-- 这是执行 init [1..10]
的 ..
部分所必需的
Num a
-- 这是执行 init [1..10]
的 1
和 10
部分所必需的
Enum [[a]]
-- 如果 init n :: a
,那么要使 init n : last n
类型正确,我们必须有 last n :: [a]
,因此第二次出现 n
必须有 n :: [[a]]
;那么 Enum
约束需要 last [1..10]
的 ..
部分
Num [[a]]
-- 通过类似的推理,需要执行 last [1..10]
的 1
和 10
部分
但是这些约束一起很难满足——当然在 Prelude
或 Data.List
范围内的列表中没有 Enum
和 Num
的实例。所以它抱怨。
在较大的程序中,有更多的类型信息(隐式和显式)允许编译器比提供给 GHCi 的单行代码片段更好地推断类型。因此,您在 GHCi 中看到的错误并不代表构建完整程序时通常观察到的错误。
就是说,这不是您发布错误的可怕借口:
Prelude> let n = [1..10] in (init n : last n)
<interactive>:8:1:
Non type-variable argument in the constraint: Enum [[a]]
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a. (Enum a, Enum [[a]], Num a, Num [[a]]) => [[a]]
所以你自己的行涉及到很多多态性。您有 Num a (Num a => [a]
) 的列表,其中类型变量 a
本身必须是列表,因为您在 last n :: [_] ~ a
中使用了 : last n
。因此,结合列表理解中的 ..
暗示枚举这一事实,我们就是如何得到这个可怕的信息的。
让我们看看更简单的情况,我们告诉 GHCi 我们的列表是类型 [Int]
:
Prelude> let n = [1..10] :: [Int] in (init n : last n)
<interactive>:7:44:
Couldn't match type ‘Int’ with ‘[[Int]]’
Expected type: [[[Int]]]
Actual type: [Int]
In the first argument of ‘last’, namely ‘n’
In the second argument of ‘(:)’, namely ‘last n’
啊,好多了。第 44 列是 last n
中的 n
。它说 last n :: Int
。所以 init n :
when init n :: [Int]
的类型意味着我们的 cons 函数类型是 (:) :: [Int] -> [[Int]] -> [[Int]]
。可是等等! init n :
的参数所需的 [[Int]]
与 last n
!
提供的 Int
不匹配
我有一个关于 head
、tail
、init
和 last
的问题。
GHCi 中的以下作品:
Prelude Data.List Data.Char> let n = [1..10] in (head n : tail n)
[1,2,3,4,5,6,7,8,9,10]
不出所料,我得到了整个列表。所以这也适用于 init
和 last
,
对吗?
Prelude Data.List Data.Char> let n = [1..10] in (init n : last n)
<interactive>:39:1:
Non type-variable argument in the constraint: Enum [[a]]
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a. (Enum a, Enum [[a]], Num a, Num [[a]]) => [[a]]
如果我查看函数的类型签名,那么 head
和 last
看起来一样——它们都是 return 一个元素。还有 init
和 tail
看看
相同,因为它们都 return lists.
Prelude Data.List Data.Char> :info head
head :: [a] -> a -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info tail
tail :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info init
init :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info last
last :: [a] -> a -- Defined in ‘GHC.List’
那么Non type-variable argument in the constraint: Enum [[a]]
是什么意思呢?
如果我在没有构建新列表的情况下执行 init n
或 last n
,我会得到
[1..9]
和 10
.
啊啊啊啊,:
像 x:xs
一样接受一个元素和其余元素,而不是像 xs:x
中的列表和最后一个元素。
Prelude Data.List Data.Char> :info :
data [] a = ... | a : [a] >·-- Defined in ‘GHC.Types’
infixr 5 :
data [] a = ... | a : [a] >·-- Defined in ‘GHC.Types’
infixr 5 :
我仍然想知道你是如何理解 GHCi 错误消息的,无论如何我必须等待 2 天才能接受我自己的答案。
编辑:我知道 x
和 xs
只是约定俗成的变量名,(xs:_)
会匹配头部但被命名为 unconventionally/confusingly。
编辑 2:我赞成并接受了 Daniel Wagner 的回答,因为他逐步解释了错误消息。非常好!谢谢!
确实 head
/last
和 tail
/init
具有相同的类型。因此,如果您只是将 last
换成 head
,将 init
换成 tail
,您就不会有问题:
> let n = [1..10] in last n : init n
[10,1,2,3,4,5,6,7,8,9]
但你没有。您同时交换了 和另一个 :您将参数的顺序更改为 :
。碰巧 :
不接受两个相同类型的参数:
> :t (:)
(:) :: a -> [a] -> [a]
所以这最后一次交换是不对的!事实上,如果你给 n
一个稍微更具体的类型签名,ghci 会给出更好的错误:
> let n :: [Integer]; n = [1..10] in init n : last n
<interactive>:1:50:
Couldn't match type ‘Integer’ with ‘[[Integer]]’
Expected type: [[[Integer]]]
Actual type: [Integer]
In the first argument of ‘last’, namely ‘n’
In the second argument of ‘(:)’, namely ‘last n’
这个错误仍然不是 100% 清楚,但我认为有点令人费解的是你可以看出它在抱怨什么:因为 init n :: [Integer]
和 (:) :: [Integer] -> [[Integer]] -> [[Integer]]
,它期待 last n :: [[Integer]]
因此 n :: [[[Integer]]]
。但是你明明说了n :: [Integer]
,冲突了
现在,在您的案例中它实际给您的错误是什么?好吧,线索在 [1..10]
:
> :t [1..10]
[1..10] :: (Enum t, Num t) => [t]
注意 [1..10]
是多态的。此外,它在您的表达式中使用了两次,因此可以在两次使用中给它 separate 单态类型。所以 [1..10]
在 sequel!
现在我想您可以开始查看您遇到的错误是从哪里来的。它试图找到一个类型 a
用于:
Enum a
-- 这是执行init [1..10]
的 Num a
-- 这是执行init [1..10]
的 Enum [[a]]
-- 如果init n :: a
,那么要使init n : last n
类型正确,我们必须有last n :: [a]
,因此第二次出现n
必须有n :: [[a]]
;那么Enum
约束需要last [1..10]
的 Num [[a]]
-- 通过类似的推理,需要执行last [1..10]
的
..
部分所必需的
1
和 10
部分所必需的
..
部分
1
和 10
部分
但是这些约束一起很难满足——当然在 Prelude
或 Data.List
范围内的列表中没有 Enum
和 Num
的实例。所以它抱怨。
在较大的程序中,有更多的类型信息(隐式和显式)允许编译器比提供给 GHCi 的单行代码片段更好地推断类型。因此,您在 GHCi 中看到的错误并不代表构建完整程序时通常观察到的错误。
就是说,这不是您发布错误的可怕借口:
Prelude> let n = [1..10] in (init n : last n)
<interactive>:8:1:
Non type-variable argument in the constraint: Enum [[a]]
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a. (Enum a, Enum [[a]], Num a, Num [[a]]) => [[a]]
所以你自己的行涉及到很多多态性。您有 Num a (Num a => [a]
) 的列表,其中类型变量 a
本身必须是列表,因为您在 last n :: [_] ~ a
中使用了 : last n
。因此,结合列表理解中的 ..
暗示枚举这一事实,我们就是如何得到这个可怕的信息的。
让我们看看更简单的情况,我们告诉 GHCi 我们的列表是类型 [Int]
:
Prelude> let n = [1..10] :: [Int] in (init n : last n)
<interactive>:7:44:
Couldn't match type ‘Int’ with ‘[[Int]]’
Expected type: [[[Int]]]
Actual type: [Int]
In the first argument of ‘last’, namely ‘n’
In the second argument of ‘(:)’, namely ‘last n’
啊,好多了。第 44 列是 last n
中的 n
。它说 last n :: Int
。所以 init n :
when init n :: [Int]
的类型意味着我们的 cons 函数类型是 (:) :: [Int] -> [[Int]] -> [[Int]]
。可是等等! init n :
的参数所需的 [[Int]]
与 last n
!
Int
不匹配