元组 #
一、元组基础 #
1.1 什么是元组 #
元组是固定数量、可以不同类型的值的组合:
haskell
-- 二元组(对)
pair :: (Int, String)
pair = (1, "hello")
-- 三元组
triple :: (Int, String, Bool)
triple = (1, "hello", True)
-- 单元类型(零元组)
unit :: ()
unit = ()
1.2 元组特点 #
haskell
-- 元组可以包含不同类型
mixed :: (Int, String, Bool, Double)
mixed = (1, "hello", True, 3.14)
-- 元组长度固定
-- 不能动态添加或删除元素
-- 元组类型由长度和元素类型共同决定
-- (Int, String) 和 (String, Int) 是不同类型
-- (Int, String) 和 (Int, String, Bool) 是不同类型
1.3 元组类型语法 #
haskell
-- 类型语法
(类型1, 类型2, ...)
-- 示例
(Int, String) -- 二元组
(Int, String, Bool) -- 三元组
(Int, Int, Int, Int) -- 四元组
() -- 单元类型
二、元组构造 #
2.1 直接构造 #
haskell
-- 二元组
point2D :: (Double, Double)
point2D = (1.0, 2.0)
-- 三元组
point3D :: (Double, Double, Double)
point3D = (1.0, 2.0, 3.0)
-- 嵌套元组
nested :: ((Int, Int), String)
nested = ((1, 2), "hello")
2.2 元组构造器 #
haskell
-- 二元组构造器
(,) :: a -> b -> (a, b)
-- 使用
pair = (,) 1 "hello" -- (1, "hello")
-- 三元组构造器
(,,) :: a -> b -> c -> (a, b, c)
triple = (,,) 1 "hello" True -- (1, "hello", True)
-- 四元组构造器
(,,,) :: a -> b -> c -> d -> (a, b, c, d)
2.3 部分应用 #
haskell
-- 创建特定类型的元组构造器
makePair :: Int -> (Int, String)
makePair = (, "default")
-- 使用
result = makePair 5 -- (5, "default")
-- 另一个例子
withTimestamp :: String -> (String, Int)
withTimestamp s = (s, getCurrentTime)
where getCurrentTime = 12345
三、元组访问 #
3.1 fst和snd #
haskell
-- fst:获取二元组第一个元素
fst (1, "hello") -- 1
-- snd:获取二元组第二个元素
snd (1, "hello") -- "hello"
-- 注意:只适用于二元组
-- fst (1, 2, 3) -- 错误!
3.2 模式匹配访问 #
haskell
-- 函数参数中模式匹配
first :: (a, b) -> a
first (x, _) = x
second :: (a, b) -> b
second (_, y) = y
-- 三元组
third :: (a, b, c) -> c
third (_, _, z) = z
-- 解构元组
sumPair :: (Int, Int) -> Int
sumPair (x, y) = x + y
3.3 let和where中解构 #
haskell
-- let中解构
result = let (x, y) = (1, 2) in x + y -- 3
-- where中解构
distance :: (Double, Double) -> (Double, Double) -> Double
distance p1 p2 = sqrt (dx * dx + dy * dy)
where
(x1, y1) = p1
(x2, y2) = p2
dx = x2 - x1
dy = y2 - y1
四、元组函数 #
4.1 基本函数 #
haskell
-- swap:交换二元组
swap :: (a, b) -> (b, a)
swap (x, y) = (y, x)
-- 示例
swap (1, "hello") -- ("hello", 1)
-- curry:将元组参数转为柯里化
curry :: ((a, b) -> c) -> a -> b -> c
curry f x y = f (x, y)
-- uncurry:将柯里化转为元组参数
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f (x, y) = f x y
4.2 curry和uncurry示例 #
haskell
-- 使用curry
addPair :: (Int, Int) -> Int
addPair (x, y) = x + y
addCurried :: Int -> Int -> Int
addCurried = curry addPair
-- 使用
addCurried 1 2 -- 3
-- 使用uncurry
add :: Int -> Int -> Int
add x y = x + y
addUncurried :: (Int, Int) -> Int
addUncurried = uncurry add
-- 使用
addUncurried (1, 2) -- 3
4.3 元组映射 #
haskell
-- 对二元组两个元素应用函数
both :: (a -> b) -> (a, a) -> (b, b)
both f (x, y) = (f x, f y)
-- 示例
both (*2) (3, 4) -- (6, 8)
-- 对第一个元素应用函数
first :: (a -> a') -> (a, b) -> (a', b)
first f (x, y) = (f x, y)
-- 对第二个元素应用函数
second :: (b -> b') -> (a, b) -> (a, b')
second f (x, y) = (x, f y)
-- 示例
first (*2) (3, 4) -- (6, 4)
second (*2) (3, 4) -- (3, 8)
五、元组与列表 #
5.1 zip和unzip #
haskell
-- zip:将两个列表配对成元组列表
zip :: [a] -> [b] -> [(a, b)]
-- 示例
zip [1, 2, 3] ['a', 'b', 'c'] -- [(1, 'a'), (2, 'b'), (3, 'c')]
-- unzip:将元组列表解成两个列表
unzip :: [(a, b)] -> ([a], [b])
-- 示例
unzip [(1, 'a'), (2, 'b'), (3, 'c')] -- ([1, 2, 3], "abc")
5.2 zip3和unzip3 #
haskell
-- zip3:三个列表配对
zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
-- 示例
zip3 [1, 2, 3] ['a', 'b', 'c'] [True, False, True]
-- [(1, 'a', True), (2, 'b', False), (3, 'c', True)]
-- unzip3:三元组列表解成三个列表
unzip3 :: [(a, b, c)] -> ([a], [b], [c])
-- 示例
unzip3 [(1, 'a', True), (2, 'b', False)]
-- ([1, 2], "ab", [True, False])
5.3 元组列表操作 #
haskell
-- 提取元组列表的第一个元素
firsts :: [(a, b)] -> [a]
firsts = map fst
-- 提取元组列表的第二个元素
seconds :: [(a, b)] -> [b]
seconds = map snd
-- 示例
firsts [(1, 'a'), (2, 'b'), (3, 'c')] -- [1, 2, 3]
seconds [(1, 'a'), (2, 'b'), (3, 'c')] -- "abc"
六、关联列表 #
6.1 什么是关联列表 #
关联列表是元组列表,用于表示键值对:
haskell
-- 关联列表
phoneBook :: [(String, String)]
phoneBook =
[ ("Alice", "123-4567")
, ("Bob", "234-5678")
, ("Charlie", "345-6789")
]
6.2 lookup函数 #
haskell
-- lookup:在关联列表中查找
lookup :: Eq a => a -> [(a, b)] -> Maybe b
-- 示例
lookup "Alice" phoneBook -- Just "123-4567"
lookup "David" phoneBook -- Nothing
6.3 自定义关联列表函数 #
haskell
-- 更新关联列表
update :: Eq a => a -> b -> [(a, b)] -> [(a, b)]
update key value assoc = (key, value) : filter ((/= key) . fst) assoc
-- 删除键
deleteKey :: Eq a => a -> [(a, b)] -> [(a, b)]
deleteKey key = filter ((/= key) . fst)
-- 获取所有键
keys :: [(a, b)] -> [a]
keys = map fst
-- 获取所有值
values :: [(a, b)] -> [b]
values = map snd
七、实践示例 #
7.1 坐标点 #
haskell
-- 二维点
type Point = (Double, Double)
-- 距离
distance :: Point -> Point -> Double
distance (x1, y1) (x2, y2) = sqrt ((x2 - x1)^2 + (y2 - y1)^2)
-- 中点
midpoint :: Point -> Point -> Point
midpoint (x1, y1) (x2, y2) = ((x1 + x2) / 2, (y1 + y2) / 2)
-- 移动
translate :: Double -> Double -> Point -> Point
translate dx dy (x, y) = (x + dx, y + dy)
7.2 学生数据 #
haskell
-- 学生数据
type Student = (String, Int, Int) -- (姓名, 数学, 英语)
-- 计算总分
totalScore :: Student -> Int
totalScore (_, math, eng) = math + eng
-- 计算平均分
averageScore :: Student -> Double
averageScore s = fromIntegral (totalScore s) / 2
-- 判断是否及格
isPassing :: Student -> Bool
isPassing (_, math, eng) = math >= 60 && eng >= 60
-- 获取学生姓名
getName :: Student -> String
getName (name, _, _) = name
7.3 复数运算 #
haskell
-- 复数表示
type Complex = (Double, Double) -- (实部, 虚部)
-- 加法
addComplex :: Complex -> Complex -> Complex
addComplex (a, b) (c, d) = (a + c, b + d)
-- 乘法
mulComplex :: Complex -> Complex -> Complex
mulComplex (a, b) (c, d) = (a * c - b * d, a * d + b * c)
-- 模
magnitude :: Complex -> Double
magnitude (a, b) = sqrt (a * a + b * b)
-- 共轭
conjugate :: Complex -> Complex
conjugate (a, b) = (a, -b)
7.4 统计数据 #
haskell
-- 统计数据
type Stats = (Int, Int, Int, Int) -- (最小值, 最大值, 总和, 数量)
-- 计算统计
stats :: [Int] -> Stats
stats [] = error "empty list"
stats xs = (minimum xs, maximum xs, sum xs, length xs)
-- 计算平均值
average :: Stats -> Double
average (_, _, total, count) = fromIntegral total / fromIntegral count
-- 格式化输出
showStats :: Stats -> String
showStats (minVal, maxVal, total, count) =
"Min: " ++ show minVal ++
", Max: " ++ show maxVal ++
", Sum: " ++ show total ++
", Count: " ++ show count
八、元组最佳实践 #
8.1 何时使用元组 #
haskell
-- 好:临时组合少量数据
getMinMax :: [Int] -> (Int, Int)
getMinMax xs = (minimum xs, maximum xs)
-- 好:返回多个值
divideWithRemainder :: Int -> Int -> (Int, Int)
divideWithRemainder x y = (x `div` y, x `mod` y)
-- 不好:大量元素或需要命名字段
-- 使用data或record代替
data Person = Person
{ name :: String
, age :: Int
, email :: String
}
8.2 类型别名 #
haskell
-- 使用类型别名提高可读性
type Point = (Double, Double)
type Name = String
type Age = Int
-- 但不创建新类型
-- type只是别名
8.3 模式匹配 #
haskell
-- 好:使用模式匹配解构
process :: (Int, String) -> String
process (n, s) = s ++ ": " ++ show n
-- 不好:使用fst和snd链式调用
processBad pair = snd pair ++ ": " ++ show (fst pair)
九、总结 #
元组要点:
- 定义:固定数量、可不同类型的组合
- 构造:
(值1, 值2, ...) - 访问:
fst、snd(仅二元组)、模式匹配 - 函数:
curry、uncurry、swap - 与列表:
zip、unzip、关联列表 - 最佳实践:少量元素、临时组合、使用类型别名
掌握元组后,让我们继续学习类型系统。
最后更新:2026-03-27