如何避免“异常:Prelude.head:空列表”? - 哈斯克尔

时间:2016-10-04 23:23:27

标签: list haskell

大家晚上好,我是哈斯凯尔的新手。我试图总结一个读取字符串Unicode值的列表并将它们存储在一个列表中,然后将整数加起来。

 getLetterUnicodeValue :: Char -> Int
 getLetterUnicodeValue l = (ord l) - 64

 unicodeValueList :: String -> [Int]
 unicodeValueList x = getLetterUnicodeValue (head x) : unicodeValueList (tail x)

 total :: [Int] -> Int
 total []     = 0
 total x = (head x) + (total (tail x))

当字符串到达​​最后一个字符并且总结功能无法成功执行时,我得到了空列表的错误。有什么办法可以在函数unicodeValueList结束时停止它。

*** Exception: Prelude.head: empty list

2 个答案:

答案 0 :(得分:5)

避免此异常的最可靠方法是不使用head。相反,您可以使用模式匹配来获取列表的头部和尾部:

unicodeValueList (x:xs) = getLetterUnicodeValue x : unicodeValueList xs

total (x:xs) = x + total xs

这种方式xxs只有在列表非空时才可用,并且保证您不会意外地访问空列表的头部或尾部。

当然,现在您会收到一个警告,指出模式匹配不完整:您没有指定列表为空时应该发生什么。当然,在此之前也是如此,但是现在您使用模式匹配,编译器实际上可以看到它并向您发出警告(而前一代码在运行时崩溃而没有任何预先警告)。

那么当列表为空时应该怎么做?好吧,空字符串不包含unicode值,对吧?所以它应该在输入为空时返回空列表:

unicodeValueList [] = []

当然,您不需要模式匹配来修复错误。您可以使用if确保在列表不为空时只调用headtail。但是如果你这样做,编译器就无法验证你的检查是否有序。如果您使用模式匹配并完全避免不安全的headtail函数,您将永远无法意外访问空列表的头部或尾部,编译器将警告您,如果您永远不要忘记列表可能是空的。

答案 1 :(得分:2)

是的,您只需要在unicodeValueList

中进行模式匹配
unicodeValueList :: String -> [Int]
unicodeValueList [] = []
unicodeValueList (x:xs) = getLetterUnicodeValue x : unicodeValueList xs

请注意,这可以更好地编写为unicodeValueList = map getLetterUnicodeValue。你得到head错误的原因是你的递归没有基本情况 - 它一直试图递归,即使它已经到达空列表。