Новичок в Хаскеле. Я создал массив символов и пытаюсь найти лучший способ изменить данные в этом массиве с учетом определенного индекса.
У меня уже есть созданный массив, который я передаю в метод ниже в моем файле main. Эта часть работает нормально. Я просто не могу понять, как манипулировать данными в каждом индексе. В этом конкретном случае я пытаюсь переопределить значение, если приведенное ниже условие выполняется с пробелом. И я хочу вернуть это на свой основной, чтобы я мог распечатать обновленную доску.
import System.Random
import Control.Monad
import Data.Array
import Data.List
modifyArray :: Array Int Char -> Int -> IO (Array Int Char)
modifyArray arr i =
if i > 0 then
if i `mod` 102 == 1 then do
(MODIFY ARRAY AT INDEX:i to equal ' ')
else modifyArray arr (i-1)
else arr
На самом деле вам не нужно изменять массив. Просто сделайте так, чтобы ваша функция создала новый массив с нужными вам изменениями, например:
modifyArray :: Array Int Char -> Int -> Array Int Char
modifyArray arr i =
if i > 0 then
if i `mod` 102 == 1 then
arr // [(i, ' ')]
else modifyArray arr (i-1)
else arr
Data.Array
включает оператор //
, что означает «взять массив слева, применить изменения справа и вернуть результат». Кроме того, поскольку эта функция не выполняет никакого ввода-вывода, нет необходимости использовать «do» или иметь IO в сигнатуре типа.
Спасибо, Джо, было бы невероятно эффективнее использовать MArray, как сказал Уилл?
@DevG Это зависит от многих деталей вашей программы, но даже если это так, то, что вы делаете, кажется достаточно небольшим, чтобы это не было большой разницей, и это добавит много сложности, особенно для новичка.
Если вы планируете обновлять свой массив всего несколько раз, вы можете использовать Оператор (//) :: Ix i => Array i e -> [(i, e)] -> Array i e
, как говорит @JosephSible.
Однако, как обсуждается в вопрос "Насколько быстро Data.Array
?", это не очень эффективно, если вы хотите обновить свой массив часто:
Note that
//
is probably O(n) though because it has to traverse the list (just like an imperative program would). If you need a lot of mutation you can useMArray
orMVector
.
Таким образом, это означает, что для больших массивов обновление массива может занять некоторое время, поскольку каждое обновление приводит к созданию копировать исходного массива и изменению этого конкретного значения.
IOArray
является особым типом такого MArray
. Таким образом, мы можем определить modifyArray
с помощью writeArray :: (MArray a e m, Ix i) => a i e -> i -> e -> m ()
:
modifyArray :: IOArray Int Char -> Int -> IO (IOArray Int Char)
modifyArray arr idx | i < 0 = return arr
| mod i 102 == 1 = writeArray arr idx ' ' >> return arr
| otherwise = modifyarr arr idx
Обратите внимание, что здесь возвращать массив не обязательно, по сути, IOArray
можно рассматривать как ссылку на изменяемый массив, поэтому после операции writeArray
arr
ссылается на измененный массив. Если вы используете это в более крупной операции IO
, то после modifyArray
этот массив будет изменен.
Здесь вы используете цикл для получения наибольшего индекса i
, который имеет div i 102 == 1
и меньше или равен начальному индексу. Однако вы можете улучшить это, вычитая результат по модулю:
modifyArray :: IOArray Int Char -> Int -> IO (IOArray Int Char)
modifyArray arr idx | i < 0 = return arr
| otherwise = writeArray arr (idx - mod (idx-1) 102) ' ' >> return arr
Если вы хотите изменить значение массива, возможно, лучше рассмотреть
MArray
(изменяемый массив) вместоArray
, посколькуArray
приведет к копировать.