Haskell: изменение значения по определенному индексу массива

Новичок в Хаскеле. Я создал массив символов и пытаюсь найти лучший способ изменить данные в этом массиве с учетом определенного индекса.

У меня уже есть созданный массив, который я передаю в метод ниже в моем файле 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   

Если вы хотите изменить значение массива, возможно, лучше рассмотреть MArray (изменяемый массив) вместо Array, поскольку Array приведет к копировать.

Willem Van Onsem 27.05.2019 22:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
686
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

На самом деле вам не нужно изменять массив. Просто сделайте так, чтобы ваша функция создала новый массив с нужными вам изменениями, например:

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 27.05.2019 22:50

@DevG Это зависит от многих деталей вашей программы, но даже если это так, то, что вы делаете, кажется достаточно небольшим, чтобы это не было большой разницей, и это добавит много сложности, особенно для новичка.

Joseph Sible-Reinstate Monica 27.05.2019 23:07

Если вы планируете обновлять свой массив всего несколько раз, вы можете использовать Оператор (//) :: 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 use MArray or MVector.

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

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 можно рассматривать как ссылку на изменяемый массив, поэтому после операции writeArrayarr ссылается на измененный массив. Если вы используете это в более крупной операции 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

Другие вопросы по теме