包含head和tail函数的函数会抛出空列表错误

时间:2018-03-17 17:44:54

标签: haskell functional-programming

我尝试解决Advent of Code 2017中的第一个问题,并提出以下解决方案来计算所需的值:

checkRepetition :: [Int] -> Bool
checkRepetition [] = False
checkRepetition (x:xs)
 | x == ( head xs ) = True
 | otherwise = False

test :: [Int] -> Int
test [] = 0
test [x] = 0
test xs
 | checkRepetition xs == True = ((head xs)*a) + (test (drop a xs))
 | otherwise = test (tail xs)
  where
   a = (go (tail xs)) + 1
   go :: [Int] -> Int
   go [] = 0
   go xs
    | checkRepetition xs == True = 1 + ( go (tail xs) )
    | otherwise = 0

但是,当我提供包含[1,3,3]等重复数字的输入时,会出现错误

  

***例外:Prelude.head:空列表

然而,在1.5小时内,我无法弄清楚这个错误的确切位置。我的意思是test函数中使用的任何函数都有[]的定义,但它仍会抛出此错误,那么问题是什么?

请注意,我已查看this个问题,并且在给定的答案中,建议不要使用headtail函数,但我已针对各种函数测试了这些函数输入,它们不会抛出任何错误,那究竟是什么问题?

我将不胜感激任何帮助或提示。

2 个答案:

答案 0 :(得分:1)

正如评论中指出的那样,问题在于:

checkRepetition (x:xs)
 | x == ( head xs ) = True

xs不能保证是非空列表(单元素列表写为x:[],因此(x:xs)模式与xs = []匹配,并且在空列表上调用head是运行时错误。

您可以通过将模式更改为仅匹配2+元素列表来处理此问题。

checkRepetition []        = False
checkRepetition [_]       = False
checkRepetition (x1:x2:_) = x1 == x2
-- No need for the alternations on this function, by the way.

那就是说,你的算法似乎不必要复杂。您所要做的就是检查 next 值是否相等,如果是,则将当前值添加到总数中。假设您可以自己获取String -> [Int],请考虑以下内容:

filteredSum :: [Int] -> Int
filteredSum []            = 0  -- by definition, zero- and one-element lists
filteredSum [_]           = 0  -- cannot produce a sum, so special case them here
filteredSum xss@(first:_) = go xss
  where
  -- handle all recursive cases
  go (x1:xs@(x2:_)) | x1 == x2   = x1 + go xs
                    | otherwise  =      go xs
  -- base case
  go [x]            | x == first = x  -- handles last character wrapping
                    | otherwise  = 0  -- and if it doesn't wrap
  -- this should be unreachable
  go []             = 0

对于它的价值,我认为最好在Maybe monad中工作并在Maybe [Int] -> Maybe Int上操作,但幸运的是,这很容易,因为Maybe是一个算子。

digitToMaybeInt :: Char -> Maybe Int
digitToMaybeInt '0' = Just 0
digitToMaybeInt '1' = Just 1
digitToMaybeInt '2' = Just 2
digitToMaybeInt '3' = Just 3
digitToMaybeInt '4' = Just 4
digitToMaybeInt '5' = Just 5
digitToMaybeInt '6' = Just 6
digitToMaybeInt '7' = Just 7
digitToMaybeInt '8' = Just 8
digitToMaybeInt '9' = Just 9
digitToMaybeInt _   = Nothing

maybeResult :: Maybe Int
maybeResult = fmap filteredSum . traverse digitToMaybeInt $ input

result :: Int
result = case maybeResult of
         Just x  -> x
         Nothing -> 0
-- this is equivalent to `maybe 0 id maybeResult`

答案 1 :(得分:0)

感谢您的链接。我先到那里去收集目的。 我假设输入将是一个字符串。下面的辅助函数构造了一个数字列表,用于汇总谓词是否为True,即压缩值是否相等,即每个数字与每个连续数字(该对)进行比较。

辅助函数'nl'使用数字列表调用主函数'invcap'Inverse Captcha。

nl函数是列表解析。 invcap函数是列表解析。也许这个问题的逻辑是错误的。过于复杂的逻辑更容易引入错误。当逻辑不繁琐时,证明非常容易。

主要功能“invcap”

invcap l = sum [ x | (x,y) <- zip l $ (tail l) ++ [head l], x == y]

将字符串转换为数字列表并使用数字列表调用invcap的辅助函数。

nl cs = invcap [ read [t] :: Int | t <- cs]

调用示例 前奏&GT; nl“91212129”...... 9'''''''''' 前奏&GT; nl“1122”...... 3