【发布时间】:2023-03-11 09:35:01
【问题描述】:
我正在寻找一种将列表转换为 n 元组的方法,其中一个列表用于不相交联合中的 n 个构造函数中的每一个。标准库专门为Eithers 定义了类似的函数:
partitionEithers :: [Either a b] -> ([a], [b])
我正在寻找解决具有以下要求的广义问题的技术:
- 写起来方便
- 尽可能少的样板代码
- 一次性处理列表
- 允许使用数据类型泛型、元编程、现有库等
示例
这是一个包含两个建议解决方案的示例规范:
partitionSum :: [MySum] -> ([A], [B], [C], [D])
data MySum
= CaseA A
| CaseB B
| CaseC C
| CaseD D
data A = A deriving Show
data B = B deriving Show
data C = C deriving Show
data D = D deriving Show
-- expect "([A,A],[B,B,B],[],[D])"
test :: IO ()
test = print . partitionSum $
[CaseD D, CaseB B, CaseA A, CaseA A, CaseB B, CaseB B]
第一次尝试:n 个遍历列表 n 次的列表推导式。
partitionSum1 :: [MySum] -> ([A], [B], [C], [D])
partitionSum1 xs =
( [a | CaseA a <- xs]
, [b | CaseB b <- xs]
, [c | CaseC c <- xs]
, [d | CaseD d <- xs]
)
第二次尝试:单次遍历输入列表。我必须手动将状态穿过折叠,这使得解决方案有点重复且写起来很烦人。
partitionSum2 :: [MySum] -> ([A], [B], [C], [D])
partitionSum2 = foldr f ([], [], [], [])
where
f x (as, bs, cs, ds) =
case x of
CaseA a -> (a : as, bs, cs, ds)
CaseB b -> (as, b : bs, cs, ds)
CaseC c -> (as, bs, c : cs, ds)
CaseD d -> (as, bs, cs, d : ds)
【问题讨论】:
-
一个想法可能是使用 template Haskell 来构建函数本身,尽管模板 Haskell 是一个有争议的话题。
标签: haskell functional-programming