P01 - 取列表最后一个元素
Last element of a list
官方模块:
Problems.P01核心函数:myLast
题目描述
找出列表的最后一个元素。如果列表为空,返回 Nothing。
这是列表类问题中最基础的一道,目的是熟悉 Haskell 的递归模式匹配和 Maybe 类型。
函数签名
1myLast :: [a] -> Maybe a
输入一个列表,返回 Just 最后一个元素,空列表返回 Nothing。
思路
Haskell 处理列表的经典手法是递归 + 模式匹配。列表有两种 case:
- 空列表
[]——直接返回Nothing - 列表有元素——需要区分最后一个元素和中间元素:
- 只有一个元素
[x](即x:[]),那x就是最后一个,返回Just x - 至少有两个元素
x:xs,那就递归地找xs的最后一个元素
- 只有一个元素
实现
方法一:标准递归
这是最直接、最符合 Haskell 风格的写法:
1myLast :: [a] -> Maybe a
2myLast [] = Nothing
3myLast [x] = Just x
4myLast (_:xs) = myLast xs
模式匹配天然处理了三种情况:
[]——空列表[x]——单元素列表,等价于(x:[])(_:xs)——至少两个元素,用_忽略头部,递归处理尾部
这种写法的好处是可读性极强,几乎就是题目描述的直接翻译。
方法二:使用 last + Maybe 包装
Haskell 标准库已经有 Prelude.last 函数,但它对空列表抛异常。我们可以封装一层:
1import Prelude hiding (last)
2
3myLast :: [a] -> Maybe a
4myLast [] = Nothing
5myLast xs = Just (Prelude.last xs)
或者更紧凑地用 maybe 和 listToMaybe(来自 Data.Maybe):
1import Data.Maybe (listToMaybe)
2
3myLast :: [a] -> Maybe a
4myLast = listToMaybe . reverse
reverse 把列表反转,listToMaybe 取反转后列表的第一个元素(即原列表的最后一个),对空列表自动返回 Nothing。
方法三:使用 foldl'
函数式风格还可以用 fold 实现:
1myLast :: [a] -> Maybe a
2myLast [] = Nothing
3myLast xs = Just . foldl' (\_ x -> x) (head xs) $ xs
但这种方法不如模式匹配直观,仅供理解 fold 的参考。
测试
用官方给出的例子验证:
1-- 基础测试
2>>> myLast [1,2,3,4]
3Just 4
4
5>>> myLast ['x','y','z']
6Just 'z'
7
8>>> myLast []
9Nothing
10
11-- 单元素列表
12>>> myLast [42]
13Just 42
14
15-- 嵌套列表(注意类型是 List of Lists)
16>>> myLast [[1,2],[3,4]]
17Just [3,4]
要点总结
- 模式匹配是处理列表递归的基础工具,
[]、[x]、(x:xs)三种模式覆盖所有情况 - 用
Maybe类型处理「可能失败」的操作,是 Haskell 的最佳实践,比抛出异常更安全 - 这道题虽然简单,但它建立的递归模式(处理空/一个/多个三种情况)会贯穿 P01-P99