Часто требуется, чтобы производительность массивов по связанным спискам не соответствовала требованию иметь прямоугольные массивы.
В качестве примера рассмотрим гексагональную сетку, показанную здесь с соседями с 1 расстоянием ячейки (3, 3) в среднем сером и с соседями 2 расстояния в светло-сером.
Скажем, нам нужен массив, который содержит для каждой ячейки индексы каждого соседа на 1 и 2 расстояния для этой ячейки. Одна небольшая проблема заключается в том, что ячейки имеют различное количество соседей по расстоянию Х - ячейки на границе сетки будут иметь меньше соседей, чем ячейки, расположенные ближе к центру сетки.
(Мы хотим получить массив индексов соседей --- вместо функции от координат ячейки до индексов соседей --- из соображений производительности.)
Мы можем обойти эту проблему, отслеживая количество соседей в каждой ячейке. Скажем, у вас есть массив
neighbors2
размера R x C x N x 2
, где R
- количество строк сетки, C
для столбцов и N
- максимальное количество соседей с двумя расстояниями для любой ячейки в сетке.
Затем, сохраняя дополнительный массив n_neighbors2
размера R x C
, мы можем отслеживать, какие индексы в neighbors2
заполнены, а какие просто заполнены нулями. Например, чтобы получить соседей ячейки с двумя расстояниями (2, 5), мы просто индексируем массив таким образом:
someNeigh = neighbors2[2, 5, 0..n_neighbors2[2, 5], ..]
someNeigh
будет n_neighbors2[2, 5] x 2
массивом (или представлением) указателей, где someNeigh[0, 0]
возвращает строку первого соседа, а someNeigh[0, 1]
- столбец первого соседа и так далее.
Обратите внимание, что элементы в позициях
neighbors2[2, 5, n_neighbors2[2, 5]+1.., ..]
не имеют значения; это пространство просто заполнение, чтобы матрица оставалась прямоугольной.
При условии, что у нас есть функция для нахождения соседей d-расстояния для любой ячейки:
import Data.Bits (shift)
rows, cols = (7, 7)
type Cell = (Int, Int)
generateNeighs :: Int -> Cell -> [Cell]
generateNeighs d cell1 = [ (row2, col2)
| row2 <- [0..rows-1]
, col2 <- [0..cols-1]
, hexDistance cell1 (row2, col2) == d]
hexDistance :: Cell -> Cell -> Int
hexDistance (r1, c1) (r2, c2) = shift (abs rd + abs (rd + cd) + abs cd) (-1)
where
rd = r1 - r2
cd = c1 - c2
Как мы можем создать вышеупомянутые массивы neighbors2
и n_neighbors2
? Предположим, что мы знаем максимальное количество соседей с двумя расстояниями N
заранее. Затем можно изменить generateNeighs
, чтобы он всегда возвращал списки одинакового размера, так как оставшиеся записи можно заполнить (0, 0). Это оставляет, на мой взгляд, две проблемы:
- Нам нужна функция для заполнения
neighbors2
, которая работает не с каждым отдельным индексом, а с срезом, в нашем случае она должна заполнять одну ячейку за раз.
n_neighbors2
должен быть заполнен одновременно как neighbors2
Решение приветствуется с массивами repa
или accelerate
.