0

I am playing with a function that takes a coordinate and a matrix as input and returns a new matrix with all of the "above" boxes shifted down one into where the old coordinate used to be. Essentially this is the "popping" stage of candy crush for a single popped box.

I've written the function recursively and somehow it seems to be modifying the global variable m instead of the local one. I imagine I could probably fix it by changing the function variable to something other than m, but I was under the impression that Python disallowed this behavior and required the usage of the global keyword if you want to change global variables from within a function. However this doesn't seem to be the case for my function and I don't know if it's because of recursion or something else I've missed.

def pop(x,y, m):
  """
  while still in bounds,
  shift m[x][y-1] to m[x][y]

  when m[x][y-1] doesn't exist, replace m[x][y] with 0

  """
  if y == 0: #hit end, fill with 0
      m[y][x] = 0        
      return m

  else:
      m[y][x] = m[y-1][x]        
      return pop(x, y-1 , m)

m = [[1,2,3,4],
     [1,3,4,2],
     [4,1,2,4],
     [1,2,3,1]]

Now a = pop(2,2, m) returns the correct value for a and then checking the value of m surprisingly also gives:

m= [[1, 2, 0, 4],
    [1, 3, 3, 2],
    [4, 1, 4, 4],
    [1, 2, 3, 1]]
martineau
  • 119,623
  • 25
  • 170
  • 301
JoeTheShmoe
  • 433
  • 6
  • 13
  • 1
    "I imagine I could probably fix it by changing the function variable to something other than m, but I was under the impression that Python disallowed this behavior and required the usage of the global keyword if you want to change global variables from within a function. " No this wouldn't fix it. Note, your *global variable* did not change, it is still referring to the exact same object. But inside the function, *you changed the object*. This is always how Python works. – juanpa.arrivillaga Sep 01 '21 at 22:03
  • 1
    Your function `pop` is modifying `m` in place. If you don't want that, you must copy it before processing it. – Kefeng91 Sep 01 '21 at 22:03
  • So, the duplicate target may seem like it doesn't quite fit, but I think the accepted answer will explain to you perfectly what is going on, check it out – juanpa.arrivillaga Sep 01 '21 at 22:17
  • 1
    Also, read https://nedbatchelder.com/text/names.html for good measure – juanpa.arrivillaga Sep 01 '21 at 22:18
  • Scoping is so complicated in Python... "variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’. " Thanks everyone for the clarifications! I always struggled to understand `referenced before assignment` but I suppose this helps clear that up too – JoeTheShmoe Sep 02 '21 at 12:46
  • One remaining question @juanpa.arrivillaga, do I have to do `m = copy.deepcopy(m)` at the top of the function? It seems wasteful of memory because a copy has to be made at each layer of the stack as opposed to, for instance, making a single external copy then passing that external copy into the function as written. – JoeTheShmoe Sep 02 '21 at 12:55
  • @JoeTheShmoe scoping is very simple. If you reference a variable, it isn't implicitly global, you go through the normal lookup - local, enclosing, global, builtins. If it is in none of those, you get a NameError. If you have an *assignment to the variable in the code block*, then it is by default *local* to that scope. If you know the scope resolution order, it's simple. The one caveat is that assignments *force* variables to be local unless declared otherwise. You want to keep in mind, *variables are just names that refer to objects in a given namespace*. Objects don't exist in any scope – juanpa.arrivillaga Sep 02 '21 at 17:31
  • @JoeTheShmoe how else do you expect it to work if you don't make a copy? If you want two independent objects, then obviously, you have to have two copies. You can't have your cake and eat it itoo. In other languages like C that have call by value as an evaluation strategy, which seems to be what you were expecting, the only difference is that the copy is *implicitly made*. In Python, copies must be explicitly made – juanpa.arrivillaga Sep 02 '21 at 17:34
  • Scoping in python is apparently not so simple for a lot of people (at least it's definitely not intuitive and has many surprising properties for people new to the language and why we should switch to a new language). Likewise, I found an article that gave it as one of the major failings of the language. Also a better system would be to implicitly copy once (inside the local scope) for my use case and then reference by value down the stack. Don't know how well this strategy abstracts though. – JoeTheShmoe Sep 02 '21 at 19:53

0 Answers0