P18 - 提取子列表
Extract a slice from a list
官方模块:
Problems.P18核心函数:slice
← P17 在 N 处切分列表 | P19 旋转列表 N 位 →
题目描述
从列表中提取一段子列表,包含从第 i 个到第 k 个元素(1-based indexing,两端包含)。
函数签名
1slice :: [a] -> Int -> Int -> [a]
思路
take (k - i + 1) . drop (i - 1):
drop (i - 1)丢掉前 i-1 个元素take (k - i + 1)取剩下的前 (k - i + 1) 个元素
实现
方法一:take + drop 组合
1slice :: [a] -> Int -> Int -> [a]
2slice xs i k = take (k - i + 1) (drop (i - 1) xs)
也可以写成无参风格:
1slice :: [a] -> Int -> Int -> [a]
2slice xs i k = take (k - i + 1) . drop (i - 1) $ xs
方法二:递归
1slice :: [a] -> Int -> Int -> [a]
2slice [] _ _ = []
3slice (x:xs) 1 1 = [x]
4slice (x:xs) 1 k = x : slice xs 1 (k - 1)
5slice (x:xs) i k = slice xs (i - 1) (k - 1)
三个模式分别处理:列表耗尽、命中单元素、在范围内收集、还没到起始位置。
方法三:使用 zip 过滤
1slice :: [a] -> Int -> Int -> [a]
2slice xs i k = map snd . filter (\(idx, _) -> idx >= i && idx <= k) $ zip [1..] xs
给每个元素编号,按序号范围过滤。思路直观但不高效(需要遍历整个列表)。
边界情况
注意 i 和 k 可能超出列表范围:
1>>> slice [1,2,3] 2 10 -- k 超出范围
2[2,3]
3
4>>> slice [1,2,3] 0 2 -- i 从 0 开始(不标准,但安全)
5[1,2]
标准定义下如果 i > 列表长度,结果为 [];如果 k > 列表长度,取到末尾为止。
测试
1>>> slice "abcdefghijk" 3 7
2"cdefg"
3
4>>> slice [1,2,3,4,5] 2 4
5[2,3,4]
6
7>>> slice "hello" 1 1
8"h"
9
10>>> slice "hello" 6 10
11[]