6

New to Haskell. I have created an char array and I am trying to figure out the best way to modify data in that array given a specific index.

I already have an array created that I am passing into the below method in my main. That part is working fine. I just cant figure out how to manipulate the data at each index. In this particular case I am trying to override the value if the below condition is met with a space. And I want to return this to my main so i can print out the updated board.

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   
1
  • If you want to change the value of an array it might be better to consider an MArray (mutable array), instead of an Array, since the Array will result in a copy. Commented May 27, 2019 at 20:27

2 Answers 2

4

You don't actually need to modify the array. Just make your function create a new array that has the change you want, like this:

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 includes the // operator, which means "take the array on the left, apply the changes on the right, and return the result". Also, since this function doesn't do any IO, there's no need to use "do" or have IO in the type signature.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Jo, would it be incredibly more efficient to be using MArray like Will said?
@DevG It depends on a lot of details of your program, but even if so, what you're doing seems to be small-scale enough that it wouldn't be a big difference, and it will add a lot of complexity, especially for a novice.
2

If you plan to update your array only a few times, you can use the (//) :: Ix i => Array i e -> [(i, e)] -> Array i e operator, like @JosephSible says.

As is however discussed in the question "How fast is Data.Array?", this is not very effective if you want to update your array frequently:

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.

This thus means that for large arrays, updating the array might take some time, since each update results in making a copy of the original array, and modifying that specific value.

The IOArray is a specific type of such MArray. We can thus define the modifyArray with 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

Note that here returning the array is not necessary, in essence the IOArray can be seen as a reference to a mutable array, so after the writeArray operation, arr refers to the modified array. If you use this in a larger IO operation, then after the modifyArray, that array is modified.

You here use a loop to obtain the largest index i that has div i 102 == 1 and is less than or equal to the initial index. You can however improve that, by subtracting the result of the modulo:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.