我有这个:
get3th (_,_,a,_,_,_) = a
这在 GHCI 中工作正常,但我想用 GHC 编译它,它给出了错误。如果我想编写一个函数来获取元组的第 n 个元素并能够在 GHC 中运行,我该怎么办?我的所有程序如下所示,我该怎么办?
get3th (_,_,a,_,_,_) = a
main = do
mytuple <- getLine
print $ get3th mytuple
getLine 的类型是IO String
,因此您的程序不会键入检查,因为您提供的是 aString
而不是元组。
如果提供了正确的参数,您的程序将运行,即:
main = do
print $ get3th (1, 2, 3, 4, 5, 6)
你的问题是getLine
给你一个String
,但你想要某种元组。您可以通过将 转换为元组来解决您的问题String
——例如使用内置read
函数。这里的第三行试图将 s 解析String
为 s 的六元组Int
。
main = do
mystring <- getLine
let mytuple = read mystring :: (Int, Int, Int, Int, Int, Int)
print $ get3th mytuple
但是请注意,虽然这对于学习类型等很有用,但您永远不应该在实践中编写这种代码。至少有两个警告信号:
你有一个包含三个以上元素的元组。这样的元组很少需要,通常可以用列表、向量或自定义数据类型替换。元组很少用于临时将两种数据合并为一个值。如果您开始经常使用元组,请考虑是否可以创建自己的数据类型。
用于read
读取结构不是一个好主意。read
任何微小的错误都会使您的程序爆炸,并带有可怕的错误消息,而这通常不是您想要的。如果您需要解析结构,最好使用真正的解析器。read
对于简单的整数等就足够了,但仅此而已。
在我看来,您的困惑在于元组和列表之间。当您第一次遇到 Haskell 时,这是可以理解的混淆,因为许多其他语言只有一个类似的结构。元组使用圆括号:(1,2)
. 包含 n 个值的元组是一种类型,每个值可以是不同的类型,从而产生不同的元组类型。所以(Int, Int)
是与 不同的类型(Int, Float)
,两者都是两个元组。前奏中有一些函数在两个元组上是多态的,即fst :: (a,b) -> a
它采用第一个元素。fst
像您自己的函数一样使用模式匹配很容易定义:
fst (a,b) = a
请注意,fst (1,2)
计算结果为1
,但fst (1,2,3)
类型错误且无法编译。
现在,另一方面,列表可以是任意长度,包括零,并且仍然是相同的类型;但每个元素必须是同一类型。列表使用方括号:[1,2,3]
. 具有 type 元素的列表的类型a
被写入[a]
。列表是通过将值附加到空列表来构造的[]
,因此可以键入具有一个元素的列表[a]
,但这是语法糖a:[]
,其中:
是cons运算符,它将值附加到列表的头部。就像元组可以进行模式匹配一样,您可以使用空列表和 cons 运算符进行模式匹配:
head :: [a] -> a
head (x:xs) = x
模式匹配的手段x
是类型a
和xs
类型[a]
,是我们想要的前者head
。(这是一个前奏功能,还有一个类似的功能tail
。)
请注意,这head
是一个偏函数,因为我们无法定义它在空列表的情况下的作用。在空列表上调用它会导致运行时错误,因为您可以在 GHCi 中自行检查。更安全的选择是使用 Maybe 类型。
safeHead :: [a] -> Maybe a
safeHead (x:xs) = Just x
safeHead [] = Nothing
String
在 Haskell 中只是[Char]
. 所以所有这些列表函数都可以用于字符串,并getLine
返回一个String
.
现在,在您的情况下,您需要第三个元素。有几种方法可以做到这一点,你可以调用tail
几次然后调用head
,或者你可以像(a:b:c:xs)
. 但是前奏中还有另一个效用函数,(!!)
它得到了第n个元素。(编写这个函数是一个很好的初学者练习)。所以你的程序可以写成
main = do
myString <- getLine
print $ myString !! 2 --zero indexed
测试给出
Prelude> main
test
's'
所以请记住,元组我们()
和严格具有给定长度,但可以有不同类型的成员;而列表使用'[]',可以是任意长度,但每个元素必须是相同的类型。String
s 实际上是字符列表。
编辑
顺便说一句,如果你有兴趣,我想我会提到有一种更简洁的方式来编写这个 main 函数。
main = getLine >>= print . (!!3)