Как сортировать данные с помощью Data.Vector.Generic.Mutable? - PullRequest
9 голосов
/ 07 сентября 2010

Как можно отсортировать длинный список данных (строки, числа с плавающей запятой и т. Д.), Которые читаются из большого файла (скажем, несколько миллионов строк), с использованием объекта Data.Vector.Generic.Mutable и алгоритма сортировки из Data.Vector.Algorithms?

1 Ответ

16 голосов
/ 07 сентября 2010

Вот как это сделать в общем случае.

Во-первых, вам нужен изменяемый вектор.Вы можете построить это постепенно, когда вы сканируете файл;выделите вектор, который будет настолько большим, насколько вам нужно, и увеличьте размер и скопируйте, когда у вас закончится свободное местоИли вы можете прочитать весь файл, сосчитать разделители записей и выделить необходимое количество места сразу.Это проще, но, вероятно, не приемлемо в реальной жизни.(Стратегия расширения по мере необходимости довольно распространена; если вы когда-либо используете язык, такой как Perl, и переносите строки, прочитанные из файла, в конец массива, это то, что происходит. Perl выделяет некоторое пространство для массива, когда вызаполните его, это увеличивает количество места, выделяет новое пространство и копирует.)

В любом случае, мне лень это делать, поэтому я просто собираюсь создать вектор с некоторыми случайными числами в нем.

Для этого нам понадобится несколько библиотек:

import Control.Monad
import System.Random
import qualified Data.Vector as IV
import qualified Data.Vector.Mutable as MV
import qualified Data.Vector.Generic as V
import qualified Data.Vector.Algorithms.Intro as VA

Нам это не нужно сразу, но в конечном итоге оно нам понадобится, поэтому я подумал, что получуэто не так.

В любом случае, наш изменяемый вектор будет «нормальным» изменяемым вектором, здесь MV.MVector.

Идея изменяемого вектора состоит в том, что вы создаете егои измените его в несколько шагов.В Haskell есть несколько способов сделать этот код чистым для вызывающего кода;Один из них - сделать все внутри монады ST.Ваше действие ST создает вектор, модифицирует его и «замораживает» его в неизменный вектор.Внутри вы используете быстрые операции «изменить это местоположение в куче времени», но внешне у вас есть что-то чистое.(Прочтите статью о ST, если вам нужен аргумент о том, почему это безопасно.)

Еще один способ справиться с изменчивыми данными - просто сделать это внутри монады Все, эм, IO,Это то, что мы собираемся сделать здесь, так как это наиболее удобно.

(Data.Vector.Mutable имеет два предопределенных типа вектора для вас, IOVector и STVector. Мы используем IOVector, которыйпомещает все векторные операции в IO.)

Итак, как и 8 параграфов назад, мы собирались создать изменяемый вектор для сортировки.И вот мы:

randVector :: IO (MV.IOVector Int)
randVector = do
  v <- MV.new 10
  forM [0..9] $ \x -> do
    r <- randomIO :: IO Int
    MV.write v x r
  return v

Это действие ввода-вывода, которое возвращает новый изменяемый вектор с 10 случайными числами внутри.(Генерация случайных чисел также может быть удобно перенесена в монаду ввода-вывода, поэтому мы сделали это тоже для удобства! Это похоже на то, что мы пишем C, но с более приятным синтаксисом и большей безопасностью типов.)

Это на самом делесложная часть.Для сортировки я импортировал Data.Vector.Algorithms.Intro, который по сути является быстрой сортировкой на месте.Функция с именем sort выполняет фактическую сортировку (в какой бы монаде не был изменяемый вектор).

Действие, которое создает случайный изменяемый вектор и сортирует его по месту, выглядит следующим образом:

sort = VA.sort =<< randVector

Теперь, чтобы распечатать это, все, что нам нужно сделать, это «заморозить» вектор в неизменяемом векторе и распечатать .toList.Или вы можете просто перебрать каждый элемент и распечатать его.

Вот что я придумал в качестве примера:

main = do
  v <- randVector
  VA.sort v
  iv <- V.unsafeFreeze v :: IO (IV.Vector Int)
  print . V.toList $ iv

V.unsafeFreeze от Data.Vector.Generic (как вы взаимодействуете свсе векторные типы с одинаковым API), как и V.toList.

В любом случае, стоит отметить, что IO здесь исключительно для удобства.Поскольку вы строите вектор из данных файла, это уместно.99% времени, однако, вы должны использовать ST.В своем действии ST создайте вектор, отсортируйте его, заморозьте его и верните замороженную версию.

Аналогичный пример с использованием STVector:

randVector :: ST s (Vector Int)
randVector = do
  vec <- new 10
  rand <- newSTRef 17
  forM_ [0..9] $ \index -> do
    randN <- readSTRef rand
    let randN' = (fst . next . mkStdGen) randN
    writeSTRef rand randN'
    write vec index randN'
  unsafeFreeze vec

Затем выполните с:

*Main> runST randVector
fromList [679560,1422110406,306332632,1905242129,692062628,393451229,355476175,1240028023,873588529,1181443777] :: Data.Vector.Vector
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...