#arrays #haskell
Вопрос:
В Python мы можем создать индекс a numpy.ndarray
с кортежами, например
cube = numpy.zeros((3,3,3,))
print(cube[(0,1,2,)])
. Однако в Haskell для индексации многослойного массива это можно сделать только с помощью нескольких !!
‘s, что кажется довольно сложным.
Я пытался foldl
:
foldl
(!!)
[[[1, 2, ....], [1, 2, ....], [1, 2, ....]],
[[1, 2, ....], [1, 2, ....], [1, 2, ....]],
[[1, 2, ....], [1, 2, ....], [1, 2, ....]]]
[0, 1, 2]
Однако foldl
может применяться только к таким функциям , как a -> b -> a
, not [a] -> b -> a
. Некоторые другие информационные шоу hmatrix
могут делать то же numpy
самое, что и в python, но это применимо только к матрицам и векторам, где размерность не регулируется.
Это всегда можно сделать с помощью индексации в стиле C, т. Е. Поместить все данные в одномерный список и проиндексировать их с помощью умножения, 0 1*3 2*9
, но это кажется грубым, приводит к потере информации об измерениях и приведет к тому, что компилятор не сможет настроить их в правильном порядке.
Как это сделать более абстрактным способом?
Комментарии:
1. Что вас останавливает
index xs i j k = xs !! i !! j !! k
?2. Посмотрите на massiv , это, вероятно, самая полная реализация многомерных массивов на сегодняшний день.
3. Это не массив, это список, и вам не следует полагаться на него,
(!!)
поскольку 1) он медленный (O (n), а не O (1), как это было бы для подлинного массива) 2) это небезопасно (если вы попытаетесь получить доступ к индексу, который неприсутствует в списке, вы получаете сбой во время выполнения).
Ответ №1:
Из вопроса мне не совсем понятно, чего вы пытаетесь достичь, но если ваш вопрос касается только индексации многомерных массивов в Haskell, я постараюсь ответить на него в меру своих возможностей. Спасибо @leftaroundabout за предложение massiv
в разделе комментариев, будучи автором этой библиотеки, я склонен согласиться с его комментарием.
Одно можно сказать наверняка: по нескольким причинам вы не хотите использовать вложенные списки для создания массивов. Сложность линейной индексации и низкая производительность — лишь некоторые из этих причин.
Построение массива
Давайте посмотрим, как мы можем это сделать massiv
. Сначала я переведу ваш numpy
пример:
cube :: Array P Ix3 Float
cube = A.replicate Seq (Sz (3 :> 3 :. 3)) 0
Обратите внимание, поскольку у нас на самом деле есть типы в Haskell, нам нужно сделать некоторые примечания к тому, какой тип массива мы пытаемся создать, например. упакованный против распакованного, изменяемый против неизменяемого и т. Д. Я рекомендую ознакомиться с документацией библиотеки, чтобы получить больше информации по этим темам. Здесь я сосредоточусь на индексах, поскольку именно об этом и идет речь. Чтобы получить элемент из вышеупомянутого 3D-массива на 0-й странице, 2-й строке и 3-м столбце ( cube[(0,1,2,)]
numpy
пример from), мы можем использовать оператор времени O (1) !
с индексом, указанным в его правой части:
λ> cube ! (0 :> 1 :. 2)
0.0
Обратите внимание, что оператор индексирования !
является частичным и приведет к исключению во время выполнения при выходе за пределы:
λ> 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-...
Чего можно легко избежать с помощью более безопасного варианта !?
:
λ> cube !? (0 :> 1 :. 2) :: Maybe Float
Just 0.0
λ> cube !? (10 :> 1 :. 2) :: Maybe Float
Nothing
Синтаксис индекса
Так же, как и в случае с numpy
, можно использовать кортежи для индексации massiv
массивов, но поскольку кортежи полиморфны, проверяющему типы иногда сложнее сделать правильный вывод, кроме того, кортежи поддерживаются massiv
только в 5 измерениях. Поэтому вместо этого я покажу примеры для Ix n
типа, где n
— количество измерений, которое может быть произвольным.
При работе с плоскими векторами Int
для индексации используется regular (соответствует 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
Для двух измерений существует специальный оператор :.
(соответствует 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
Индекс для любого измерения больше 2 строится с :>
помощью operator (соответствует 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
Более подробную информацию об индексах с примерами можно найти в разделе #index на README.