我正在寻找一种方法将列表转换为n元组,每个n元组对应于一个不交并集中的n个构造函数之一。标准库定义了一个类似的函数,专门用于
第二次尝试:对输入列表进行单次遍历。我必须手动将状态穿过折叠,这使得解决方案有些重复和烦人,需要编写大量代码。
Either
类型: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)
partition :: Representable r => Foldable f => Eq (Rep r) => (a -> Rep r) -> f a -> r [a]
,基于可表示函子。 - Iceland_jack