含head/tail的函数触发空列表错误——Advent of Code 2017第1题求助
修复Haskell代码的空列表错误并完成Advent of Code 2017第1题
首先,我们来分析你代码中的空列表错误根源,然后逐步修复并完成符合题目要求的功能。
空列表错误的原因
checkRepetition的不安全调用:你的原函数在处理长度为1的列表时,会尝试调用head xs(此时xs是空列表),而head不允许作用于空列表,直接触发异常。- 未处理边界情况:递归过程中没有确保
tail和head只作用于非空列表,比如当列表只剩一个元素时,继续调用tail会得到空列表,后续操作必然出错。 - 未完成的函数拼写:
go函数中的checkRepeti是checkRepetition的拼写错误,导致函数无法正常编译。
明确题目要求(Advent of Code 2017 Day 1)
题目分为两部分:
- Part 1:给定一串数字,计算所有与下一个数字相同的数字之和;最后一个数字需要与第一个数字比较,相同则累加。
- Part 2:计算每个数字与列表对面(长度一半位置)的数字相同的和(如果列表长度为偶数)。
你的代码看起来是针对Part 1的,我们先修复并完成这部分。
修复后的代码(基于你的递归思路)
我们先修正边界情况,避免空列表操作,再添加循环比较的逻辑:
-- 判断列表前两个元素是否相同(安全处理空列表和单元素列表) checkRepetition :: [Int] -> Bool checkRepetition [] = False checkRepetition [_] = False -- 单元素没有下一个元素,无法重复 checkRepetition (x:y:_) = x == y -- 计算从当前位置开始,连续相同元素的长度 go :: [Int] -> Int go [] = 0 go [x] = 1 go (x:y:xs) | x == y = 1 + go (y:xs) | otherwise = 1 -- 核心计算函数:处理循环列表(最后一个元素和第一个元素比较) test :: [Int] -> Int test [] = 0 test [x] = 0 -- 单个元素无法和任何元素比较,总和为0 test xs = testHelper (xs ++ [head xs]) -- 把第一个元素加到末尾,实现循环比较 where testHelper [] = 0 testHelper [_] = 0 testHelper ys -- 如果当前前两个元素相同,计算连续块的总和,然后跳过整个块 | checkRepetition ys = let consecutiveLength = go ys in (head ys * consecutiveLength) + testHelper (drop consecutiveLength ys) -- 否则跳过当前元素,继续递归 | otherwise = testHelper (tail ys)
代码说明
checkRepetition:改用模式匹配直接提取前两个元素,避免head/tail的空列表风险,同时处理空列表和单元素列表的边界情况。go函数:安全计算连续相同元素的长度,同样用模式匹配处理空列表和单元素列表。test函数:通过xs ++ [head xs]将列表转为循环结构,确保最后一个元素能和第一个元素比较;testHelper递归处理每个连续块,累加符合条件的和。
测试示例
- 输入
[1,1,2,2]→ 输出6(1+1+2+2) - 输入
[1,2,1]→ 输出1(最后一个1和第一个1相同) - 输入
[1,1,1,1]→ 输出4(每个1都和下一个相同,包括最后一个和第一个)
更简洁的Haskell风格实现(推荐)
对于Advent of Code的题目,Haskell的列表推导和zip函数可以更简洁地实现需求,避免递归的边界问题:
Part 1 实现
part1 :: String -> Int part1 s = sum [read [c] | (c1, c2) <- zip s (tail s ++ [head s]), c1 == c2]
- 解释:将输入字符串转为字符对(当前字符和下一个字符,最后一个字符配对第一个字符),筛选出相同的字符对,转换为整数后求和。
Part 2 实现
part2 :: String -> Int part2 s = let n = length s half = n `div` 2 paired = zip s (drop half s ++ take half s) in sum [read [c] | (c1, c2) <- paired, c1 == c2]
- 解释:将列表分为前后两半,把后半部分拼到前半部分前面,形成每个元素与对面元素的配对,筛选相同的求和。
内容的提问来源于stack exchange,提问作者Our
相关产品推荐
相关产品推荐

