P20 - 删除第 k 个元素
Remove the K’th element from a list
官方模块:
Problems.P20核心函数:removeAt
← P19 旋转列表 N 位 | P21 在指定位置插入元素 →
题目描述
删除列表中第 k 个元素(1-based indexing)。返回被删除的元素和剩余列表组成的元组。
函数签名
1removeAt :: Int -> [a] -> (a, [a])
注意:k 是第一个参数,列表是第二个参数。
思路
用 splitAt 在 k-1 处切分:
- 前半部分:前 k-1 个元素
- 被删除元素:后半部分的第一个
- 后半部分剩余:除第一个外的剩余元素
实现
方法一:splitAt
1removeAt :: Int -> [a] -> (a, [a])
2removeAt k xs = let (left, right) = splitAt (k - 1) xs
3 in (head right, left ++ tail right)
splitAt (k-1) xs 在目标位置前切分。head right 是被删除的元素,left ++ tail right 是剩余列表。
注意:这个实现假设 k 在合法范围内。如果 k > 列表长度,
head right会抛异常。
方法二:递归
1removeAt :: Int -> [a] -> (a, [a])
2removeAt 1 (x:xs) = (x, xs)
3removeAt n (x:xs) = let (removed, rest) = removeAt (n - 1) xs
4 in (removed, x : rest)
模式匹配 1 (x:xs) 命中目标——删除 x,剩余 xs。否则递归前进。
方法三:带 Maybe 的安全版本(扩展)
官方签名不要求安全处理,但如果想避免异常:
1removeAtSafe :: Int -> [a] -> Maybe (a, [a])
2removeAtSafe _ [] = Nothing
3removeAtSafe 1 (x:xs) = Just (x, xs)
4removeAtSafe n (x:xs)
5 | n > 1 = (\(r, rest) -> (r, x : rest)) <$> removeAtSafe (n - 1) xs
6 | otherwise = Nothing
示例:用 removeAt 实现其他操作
removeAt 是列表操作的基础构建块:
1-- 删除第 k 个(只返回剩余列表)
2deleteAt k xs = snd (removeAt k xs)
3
4-- 提取第 k 个(只返回元素)
5extractAt k xs = fst (removeAt k xs)
测试
1>>> removeAt 2 "abcd"
2('b', "acd")
3
4>>> removeAt 1 [10, 20, 30]
5(10, [20, 30])
6
7>>> removeAt 3 [1, 2, 3]
8(3, [1, 2])