如何在Haskell中表示二维数组整数,以便我可以在O(1)或O(logN)中使用(i,j)访问任何元素?
使用Data.Array和Data.Vector进行定义有什么区别?哪个更实用
data Matrix = Vector (Vector Int)
如果我定义如上,那么如何制作一个可以对此矩阵进行转置的函数?
例如:换位[[1,2,3],[4,5,6],[7,8,9]]
将返回[[1,4,7],[2,5,8],[3,6,9]]
答案 0 :(得分:4)
Array
和Vector
具有相似的内部表示,但是Array
的API是专为索引操作(通过Ix
类)而Vector
具有一个更像列表的界面,并依靠融合来有效地组合这些操作。两者都允许O(1)随机访问。
顺便说一句,Array
中有base
,而Vector
定义了vector
包,因此如果您想避免使用Array
另一个依赖项。
在任何一种情况下,都可以根据索引来实现换位。使用Array
可能会更自然一些,import Data.Array (Array, (!))
import qualified Data.Array as Array
example1 :: Array (Int, Int) Char
example1 = Array.array ((0, 0), (1, 1))
[ ((0, 0), 'a'), ((0, 1), 'b')
, ((1, 0), 'c'), ((1, 1), 'd')
]
example2 :: Array (Int, Int) Int
example2 = Array.listArray ((0, 0), (2, 2))
[ 1, 2, 3
, 4, 5, 6
, 7, 8, 9
]
transpose :: Array (Int, Int) a -> Array (Int, Int) a
transpose a = Array.array bounds
[ ((row, col), a ! (col, row))
| row <- [minRow .. maxRow]
, col <- [minCol .. maxCol]
]
where
-- Note that indices need not be 0-based.
bounds@((minRow, minCol), (maxRow, maxCol)) = Array.bounds a
transpose example1 == Array.array ((0, 0), (1, 1))
[ ((0, 0), 'a'), ((0, 1), 'c')
, ((1, 0), 'b'), ((1, 1), 'd')
]
Array.elems (transpose example2) ==
[ 1, 4, 7
, 2, 5, 8
, 3, 6, 9
]
可以成对索引以创建保证为矩形的2D数组:
Vector
对于import Data.Vector (Vector, (!))
import qualified Data.Vector as Vector
-- Does not handle non-square arrays, nor
-- those with an outer dimension of zero.
transpose :: Vector (Vector a) -> Vector (Vector a)
transpose v = Vector.fromList
[ Vector.fromList
[ v ! col ! row
| col <- [0 .. maxCol]
]
| row <- [0 .. maxRow]
]
where
maxRow = Vector.length v - 1
maxCol = Vector.length (v ! 0) - 1
,您只需索引两次:
Vector (Vector a)
请注意,不能保证fromList
[ fromList ['a', 'b']
, fromList ['c']
]
不是“锯齿状”的2D数组,其中内部向量的长度是不同的,例如:
Vector (Vector a)
这仅意味着您需要检查输入的格式是否正确。此外,Array (Int, Int) a
在内存中不是连续的:它是指向元素向量的指针的数组,而Vector
是单个内存块,因此Vector a
对此造成了恒定的开销表示。如果需要考虑性能,则可以通过使用长度为rows * cols
的{{1}}并将其手动索引为row * cols + col
来删除此间接寻址;这实际上是Array
通过Ix
内部执行的操作。
答案 1 :(得分:0)
如果您使用的是平面向量,那么比坚持使用vector
包可能是一个好主意。但是,如果确实需要1个以上的维,建议您使用一个处理数组的库。我是massiv
的作者,因此我有些偏颇,但这是我推荐的库。我绝对不建议您使用array
库,尽管它已与ghc连接,但其接口非常有限且有点混乱。
例如,如果您想用massiv
转置2d矩阵,您可以这样做:
λ> arr <- resizeM (Sz2 3 3) (1 ... 9)
λ> arr
Array D Seq (Sz (3 :. 3))
[ [ 1, 2, 3 ]
, [ 4, 5, 6 ]
, [ 7, 8, 9 ]
]
λ> let arrUnboxed = computeAs U $ transpose arr
λ> arrUnboxed
Array U Seq (Sz (3 :. 3))
[ [ 1, 4, 7 ]
, [ 2, 5, 8 ]
, [ 3, 6, 9 ]
]
λ> arrUnboxed ! (2 :. 1)
6
请注意,直到调用compute
为止,都没有发生内存分配,但是一旦计算出来,就只能在O(1)中对其进行索引。同样,如果您应用setComp Par arr
,整个操作将自动为您并行化。
如果需要入门,文档中还有更多示例。