Haskell列表元素修改:匹配10时累加后续两个元素
Haskell: 替换列表中等于10的元素为自身加后续两个元素之和
问题分析
你需要遍历列表,将每个等于10的元素替换为10 + 下一个元素 + 下下个元素,其余元素保持不变。map函数无法满足需求,因为它只能单独处理每个元素,无法获取当前元素的后续元素上下文;且你原代码中的x:y:z仅绑定了列表前三个元素,遍历后续元素时会重复使用这三个值,导致逻辑错误。
递归实现方案
递归是处理Haskell列表问题的自然方式,我们可以在遍历过程中携带后续元素的信息,分情况处理:
bowling :: [Int] -> [Int] -- 空列表直接返回空 bowling [] = [] -- 只有一个元素,无法获取后续两个元素,直接返回 bowling [x] = [x] -- 只有两个元素,同样无法满足"加后续两个元素"的条件,直接返回 bowling [x, y] = [x, y] -- 列表长度≥3时,分情况处理第一个元素 bowling (x:y:z:xs) | x == 10 = (x + y + z) : bowling (y:z:xs) -- x是10,替换为x+y+z,继续处理剩余列表 | otherwise = x : bowling (y:z:xs) -- x不是10,直接保留,继续处理剩余列表
代码验证
用你的测试输入验证:
b1 = bowling [10, 9, 1, 8, 2, 10, 10, 6, 4, 5, 4, 7, 3, 3, 4, 3, 7, 1] -- 输出:[20,9,1,8,2,26,20,6,4,5,4,7,3,3,4,3,7,1]
完全符合预期结果。
原代码问题解析
你的原代码:
bowling (x:y:z:xs) = map (\x -> if (x == 10) then x + y + z else x) xs
存在两个核心问题:
- 上下文丢失:
map遍历的是xs(原列表去掉前三个元素后的部分),lambda中的x是xs的元素,但y和z始终是原列表的第二个和第三个元素,导致所有等于10的元素都会加上这两个固定值,而非自身的后续两个元素。 - 截断列表:返回结果仅包含
xs处理后的元素,原列表的前三个元素被直接丢弃,导致输出长度比输入短。
可选实现(zipWith3)
如果喜欢更简洁的写法,可以用zipWith3将原列表与它的两次tail结果绑定,生成三元组后处理:
bowling :: [Int] -> [Int] bowling lst | length lst < 3 = lst | otherwise = take (length lst) $ zipWith3 modify lst (tail lst) (tail $ tail lst) ++ drop 2 lst where modify x y z = if x == 10 then x + y + z else x
不过这种方式需要处理列表长度不足3的情况,且因为用到了length和take,对于超长列表的效率不如递归实现。
内容的提问来源于stack exchange,提问作者JSchofi95
相关产品推荐
相关产品推荐

