Multidimensional arrays

hello. I am just wondering what is difference between m*[n*[1]] and [n*[1] for k in range(m)]
Because this code:

m,n = 50,50
grid = [n*[1] for k in range(m)]
screen_size =(600,600)
w=float(screen_size[0])/n
h=float(screen_size[1])/m
def setup():
    size(screen_size[0],screen_size[1])
    noLoop()
    
def draw():
    draw_grid()
    
            
def draw_grid():
    x,y=0,0
    for row in grid:
        for ele in row:
            if ele == -1:
                fill(0)
            else:
                fill(255)    
            rect(x,y,w,h)
            x+=w
        y+=h
        x=0    
            

def get_grid_coordinates(x,y):
    return int(y/h), int(x/w)                    

def mousePressed():
        coords = get_grid_coordinates(mouseX,mouseY)
        pcoords = get_grid_coordinates(pmouseX,pmouseY)
        grid[coords[0]][coords[1]],grid[pcoords[0]][pcoords[1]] = -grid[coords[0]][coords[1]],-grid[pcoords[0]][pcoords[1]]
        redraw()

works fine (change color of clicked tile in grid) but this code:

m,n = 50,50
grid =  m*[n*[1]]
screen_size =(600,600)
w=float(screen_size[0])/n
h=float(screen_size[1])/m
def setup():
    size(screen_size[0],screen_size[1])
    noLoop()
    
def draw():
    draw_grid()
    
            
def draw_grid():
    x,y=0,0
    for row in grid:
        for ele in row:
            if ele == -1:
                fill(0)
            else:
                fill(255)    
            rect(x,y,w,h)
            x+=w
        y+=h
        x=0    
            

def get_grid_coordinates(x,y):
    return int(y/h), int(x/w)                    

def mousePressed():
        coords = get_grid_coordinates(mouseX,mouseY)
        pcoords = get_grid_coordinates(pmouseX,pmouseY)
        grid[coords[0]][coords[1]],grid[pcoords[0]][pcoords[1]] = -grid[coords[0]][coords[1]],-grid[pcoords[0]][pcoords[1]]
        redraw()

changes whole columns.
The strangest is that print([n*[1]] for k in range(m) == m*[n*[1]]) writes True.
I am using processing 4.0b8

I think you should move this to
Processing.py

the Processing thread is for java mode.
And format your code with </> trying to copy this would be a nightmare because the spaces actually are important in python…

I looked it up and python seems to have the same “problem” than Java.
Have you ever tried copying an array with:

array1=array2

You should notice that they are “linked” so any action you do with one you also do to the other this is called a deep copy.
Types like numbers or string don’t have deep copies so you can savely use:

number1=number2

What python does it duplicates the line-array however it uses a deep copy so any action you perform on one is also performed on the other.

What you are looking for is a shallow copy wich only copies the data there is actually a library for this so you can use:

import copy
m=10
n=10
grid=n*[copy.copy(m*[1])]

I don’t know if python mode of Processing has a built in function for this.

Thanks for reply and moving to python mode section.
If I am right in case m*[n*[1]] python m times append the same physical list so if one change one of them, the others will be changed too, but in case [n*[1] for k in range(m)] for each k, new list of ones is generated.
Am I right?

2 Likes

Yup! Alternatively you can turn the outer dimension a Tuple container:
tuple(n*[0] for _ in range(m))

But notice the literal 1 you’re using is immutable.
That’s why it’s a good candidate for filling up the entire container.

Otherwise, something mutable like a PVector would cause the same shallow-copy mirror-change bug:
tuple(n*[PVector()] for _ in range(m))

2 Likes

BtW, creating a 2d grid container is worth making a custom function for it: :hash:

def setup():
    global grid
    grid = createGrid(5) # 5x5 = 0
    print grid
    exit()


def createGrid(rows, cols = -1, fillValue = 0):
    if cols < 0: cols = rows
    return tuple([fillValue] * cols for _ in range(rows))
1 Like