P18 - 提取子列表

2026-09-07 00:00    #Haskell   #99题   #列表  

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)

  1. drop (i - 1) 丢掉前 i-1 个元素
  2. 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

给每个元素编号,按序号范围过滤。思路直观但不高效(需要遍历整个列表)。

边界情况

注意 ik 可能超出列表范围:

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[]

参考


← P17 在 N 处切分列表 | P19 旋转列表 N 位 →