arrayshaskell

Incremental array update - reversing an array with // operator


I'm trying to learn the proper syntax for arrays in Haskell (I know, not the most used construct in Haskell but I want to learn how to use them for when I need it). I'm trying for now to just reverse the elements in the array, using the "//" operator (all from GHC.arr).

The relevant code is as follow:

test = listArray (1,4) [1..4]

reverser x = x//[(i, x!j) | i<-[1..4], j<-[4,3..1]]

This results in: array (1,4) [(1,1),(2,1),(3,1),(4,1)], and I really don't get why. Looking at the first entry in the new array, I would expect (1, x!4) = (1, 4).

Because at some point I will need to reverse selected parts of the array, including single elements, I also tried:

reverser x = x//[(i, x!j) | i<-[1..4], j<-enumFromTo 4 1]

This resulted in: array (1,4) [(1,1),(2,2),(3,3),(4,4)]: I thought [4,3..1] was just syntaxic sugar for enumFromTo 4 1, so not only don't I understand the result, I don't see why it is different from the previous one.


Solution

  • Try taking a look at just the update part, and you'll see what's going on immediately:

    > x = listArray (1,4) [1..4]
    > [(i, x!j) | i<-[1..4], j<-[4,3..1]]
    [(1,4),(1,3),(1,2),(1,1),(2,4),(2,3),(2,2),(2,1),(3,4),(3,3),(3,2),(3,1),(4,4),(4,3),(4,2),(4,1)]
    

    Whoops! I wager that update list is a bit longer than you expected. In a list comprehension, , means a nested loop, not a parallel loop. You could turn on fancy list comprehensions and use a second |:

    > :set -XParallelListComp
    > --                    v
    > [(i, x!j) | i<-[1..4] | j<-[4,3..1]]
    [(1,4),(2,3),(3,2),(4,1)]
    

    Or you could use zipWith without any additional extensions:

    > zipWith (\i j -> (i, x!j)) [1..4] [4,3..1]
    [(1,4),(2,3),(3,2),(4,1)]
    

    But probably the best solution is to use ixmap, which was made for this, and doesn't require creating any intermediate lists:

    > ixmap (1,4) (5-) x
    array (1,4) [(1,4),(2,3),(3,2),(4,1)]