По существу последовательные преобразования массива в Repa - PullRequest
3 голосов
/ 12 июня 2011

Интересно, есть ли аналог (//) в repa?

Он необходим для преобразований массива, которые нельзя распараллелить.Например, если функция требует, чтобы весь массив изменил одну запись массива, а затем применяется к новому массиву и т. Д. (И он должен выполняться последовательно).

Ответы [ 2 ]

5 голосов
/ 12 июня 2011

(//) может быть реализовано в терминах Data.Array.Repa.fromFunction:

import Data.Array.Repa

(//) :: Shape sh => Array sh a -> [(sh,a)] -> Array sh a
(//) arr us = fromFunction (extent arr) (\sh -> case lookup sh us of
                                                 Just a  -> a
                                                 Nothing -> index arr sh)

fromFunction может быть передана функция типа Shape sh => s -> a, которая сама может использовать весьмассив.

Приведенная выше реализация выполняет все обновления за один проход.

4 голосов
/ 03 февраля 2012

Одна потенциальная проблема с (//) состоит в том, что требуется поиск по списку, чтобы найти значение для каждого элемента. Если массив или список велики, это может дорого обойтись.

Другой вариант - использовать удобную функцию из Data.Vector:

modify :: Vector v a => (forall s. Mutable v s a -> ST s ()) -> v a -> v a

Это имеет возможность сделать обновление на месте, если это безопасно. Так что-то вроде

import Data.Vector.Unboxed as V
import Data.Vector.Mutable.Unboxed as M
import Data.Array.Repa as R

(///) :: Shape sh => Array sh a -> [(sh,a)] -> Array sh a
(///) arr us = R.fromVector sh . modify f $ R.toVector arr
  where
  sh = extent arr
  f mv = forM_ us $ \(k,x) -> do
    M.write mv (R.toIndex sh k) x

На своем ноутбуке я протестировал это для массива DIM1 с 1 млн. Элементов, обновив 100 записей, и получил следующие значения: (//): 3.598973 (///): 2.0859999999999997e-3

...