转换嵌套的数据结构



我需要一个类似Vector [Vector a] -> Vector [Vector a]的函数,其中所有内容都位于同一列表索引内,但(对于每个列表索引(第j个内部向量的第I个元素变成第I个内部向量中的第j个元素我怀疑我让这件事比需要的要困难得多。

我想我特别想使用这种固定长度向量的实现,但我可以考虑其他选项。

我把它放在我为简单起见编写的Pair类中,但我的实现在很大程度上依赖于针对Pair结构的模式匹配,我认为这种方式不适用于Vec n

Pair类几乎没有相关性;它类似于Vec nat2,但我只实现了我认为需要的东西:

data Pair a where
Pair :: a -> a -> Pair a
deriving (Read, Show, Eq)
instance Functor Pair where
fmap f (Pair a1 a2) = Pair (f a1) (f a2)
instance Applicative Pair where
pure a = Pair a a
(Pair f1 f2) <*> (Pair a1 a2) = Pair (f1 a1) (f2 a2)
instance Foldable Pair where
foldr f b (Pair a1 a2) = a1 `f` (a2 `f` b)
(!) :: Pair a -> Bool -> a
(Pair a1 a2) ! b = if b then a2 else a1
universe :: Pair Bool
universe = Pair False True

工作代码:

transpose :: Pair [Pair a] -> Pair [Pair a]
transpose (Pair b1s b2s) = t' b1s b2s
where t' ((Pair a11 a12) : a1s) ((Pair a21 a22) : a2s)
= Pair ((Pair a11 a21) :) ((Pair a12 a22) :) <*> t' a1s a2s
t' [] [] = pure []
-- A runtime error here is ok; I have external guarantees that this won't happen.
t' _ _ = error "Transposition requires the lists to be of equal length."

示例:

> let subject = Pair [
Pair (1, 1, 1) (1, 1, 2),
Pair (1, 2, 1) (1, 2, 2),
Pair (1, 3, 1) (1, 3, 2)
] [
Pair (2, 1, 1) (2, 1, 2),
Pair (2, 2, 1) (2, 2, 2),
Pair (2, 3, 1) (2, 3, 2)
]
> transpose subject
↠ Pair [
Pair (1,1,1) (2,1,1),
Pair (1,2,1) (2,2,1),
Pair (1,3,1) (2,3,1)
] [
Pair (1,1,2) (2,1,2),
Pair (1,2,2) (2,2,2),
Pair (1,3,2) (2,3,2)
]

经过一些努力,我可以几乎获得一个只使用list/functor/application/able运算符而不是模式匹配的实现(因此应该适用于Vec n(,但我一直被卡住了。

如何重写我的transpose函数以处理其他结构,如Pair,但具有参数大小

我想我应该使用ZipList和一个新的类似的ZipVector来实现这一点。我们将从更标准的transpose:开始

transposeVV :: Vector (Vector a) -> Vector (Vector a)
transposeVV = getZipVector . traverse Finite

为了能够使用它,我们需要能够将两个Vector放在一起;我们将通过另外两个彼此相反的转置来实现这一点:

transposeVL :: Vector [a] -> [Vector a]
transposeVL = getZipList . traverse ZipList
transposeLV :: [Vector a] -> Vector [a]
transposeLV = getZipVector . traverse Finite

你现在想要的是这些的结合:

transposeVlV :: Vector [Vector a] -> Vector [Vector a]
transposeVlV = transposeLV . fmap transposeVV . transposeVL

好吧,我在ZipVector后面隐藏了一些功能。那是什么样子的?这很容易;然而,与ZipList相比,一个转折点是我们不能容易地构造无限向量。没什么大不了的,我们会伪造它。(很明显,你的长度索引向量不需要使用这个技巧。(

data ZipVector a
= Inf a
| Finite (Vector a)
deriving Functor
getZipVector :: ZipVector a -> Vector a
getZipVector (Inf a) = V.singleton a -- hmmmm...
getZipVector (Finite as) = as
instance Applicative ZipVector where
pure = Inf
Inf f <*> Inf x = Inf (f x)
Inf f <*> Finite xs = Finite (f <$> xs)
Finite fs <*> Inf x = Finite (($x) <$> fs)
Finite fs <*> Finite xs = Finite (V.zipWith ($) fs xs)

我们现在可以试试。首先,让我们定义您的subject:

subject :: Vector [Vector (Int, Int, Int)]
subject =
V.generate 2 $ i ->
flip map [0..2] $ j ->
V.generate 2 $ k ->
(i+1, j+1, k+1)

然后,在ghci:中

> transposeVlV subject
[[[(1,1,1),(2,1,1)],[(1,2,1),(2,2,1)],[(1,3,1),(2,3,1)]],[[(1,1,2),(2,1,2)],[(1,2,2),(2,2,2)],[(1,3,2),(2,3,2)]]]

最新更新