简体   繁体   中英

N-dimensional Array with Haskell

In Python we can create index a numpy.ndarray with tuples, like

cube = numpy.zeros((3,3,3,))
print(cube[(0,1,2,)])

. However, in Haskell, to index a multi-layered array this can only be done with multiple !! 's which seems pretty adhoc.

I tried foldl :

foldl

  (!!)

  [[[1, 2, ....], [1, 2, ....], [1, 2, ....]],
   [[1, 2, ....], [1, 2, ....], [1, 2, ....]],
   [[1, 2, ....], [1, 2, ....], [1, 2, ....]]]

  [0, 1, 2]

However foldl can only apply to functions like a -> b -> a , not [a] -> b -> a . Some other information shows hmatrix can do things like numpy in python, but it only applies to matrix and vectors, where the dimension is not adjustable.

This can always be done with C style indexing, ie put all data in a 1d list, and index them with multiplications, 0 + 1*3 + 2*9 , but it seems rude, losing the information of dimensions and will cause the compiler fail to adjust them in a proper order.

How to do this with a more abstract way?

It is not quite clear to me from the question what you are trying to achieve, but if your question is only about indexing multidimensional arrays in Haskell then I'll try to answer it to best of my ability. Thanks to @leftaroundabout for suggesting massiv in the comments section, being the author of that library I am inclined to agree with his comment.

One thing is for certain, for multiple reasons you do not want to use nested lists for the purpose of arrays. Linear indexing complexity and abysmal performance are only some of those reasons.

Constructing an array

Let's see how we can get it done with massiv . First I'll translate your numpy example:

cube :: Array P Ix3 Float
cube = A.replicate Seq (Sz (3 :> 3 :. 3)) 0

Note because we actually have types in Haskell we need to do some annotations on what type of array we are trying to construct, eg. boxed vs unboxed, mutable vs immutable etc. I recommend reading through library's documentation in order to get more info on those topics. Here I'll focus on indices, since that is what the question is about. In order to get an element from the above 3D array at 0th page, 2nd row and 3rd column (the cube[(0,1,2,)] from numpy example) we can use O(1) time operator ! with an index supplied on its right side:

λ> cube ! (0 :> 1 :. 2)
0.0

Note that indexing operator ! is partial and will result in a runtime exception on out of bounds:

λ> cube ! (10 :> 1 :. 2)
*** Exception: IndexOutOfBoundsException: (10 :> 1 :. 2) is not safe for (Sz (3 :> 3 :. 3))
CallStack (from HasCallStack):
  throwEither, called at src/Data/Massiv/Core/Common.hs:807:11 in massiv-1.0.1.0-...

Which can be easily avoid with its safer variant !? :

λ> cube !? (0 :> 1 :. 2) :: Maybe Float
Just 0.0
λ> cube !? (10 :> 1 :. 2) :: Maybe Float
Nothing

Index syntax

Same as with numpy it is possible to use tuples for indexing massiv arrays, but because tuples are polymorphic, it is sometimes trickier for the type checker to infer the right thing, also tuples are supported in massiv only up to 5 dimensions. That's why I will show examples for Ix n type instead, where n is number of dimensions, which can be arbitrary.

When working with flat vectors then regular Int is used for indexing (corresponds to Ix 1 ):

λ> let vec = makeVectorR P Seq (Sz 10) id
λ> vec
Array P Seq (Sz1 10)
  [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
λ> vec ! 7
7

For two dimensions there is a special operator :. (corresponds to Ix 2 ):

λ> let mat = makeArrayR P Seq (Sz (2 :. 10)) $ \(i :. j) -> i + j
λ> mat
Array P Seq (Sz (2 :. 10))
  [ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
  , [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
  ]
λ> mat ! (1 :. 3)
4

Index for any dimension larger than 2 is built with :> operator (corresponds to Ix n ):

λ> let arr3D = makeArrayR P Seq (Sz (3 :> 2 :. 1)) $ \(i :> j :. k) -> i + j + k
λ> arr3D ! (2 :> 1 :. 0)
3
λ> let arr4D = makeArrayR P Seq (Sz (4 :> 3 :> 2 :. 1)) $ \(h :> i :> j :. k) -> h + i + j + k
λ> arr4D ! (3 :> 2 :> 1 :. 0)
6

More info on indices with examples can be found in the README's #index section.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM