Reputation: 4461
I don't quite understand what is going with variable 'current_label', which to me seems to be defined in the enclosing code of function 'ts_r', which would make it visible from inside 'ts_r'? But when I run the code below, it complains that local variable 'current_label' is referenced before assignment... Note that it does not complain about 'visited' or 'f', and that it won't complain if I initialize 'current_label' with [len(g)].
def topological_sort(g):
visited = zeros((len(g)), dtype='int32')
f = zeros((len(g)), dtype='int32')
current_label = len(g) # [] so it is seen inside ts_r
def ts_r(n):
for nn in [v for v in g[n] if not visited[v]]:
visited[nn] = 1
ts_r(nn)
f[n] = current_label
current_label -= 1
for i in range(len(g)):
if not visited[i]:
ts_r(i)
return f
Upvotes: 1
Views: 164
Reputation: 137310
In case of visited
or f
you change mutable variables. In case of current_label
you try to re-assign value to global variable without stating it is global.
Changing variables from outside scopes does not require declaring them global, but reassigning value to a global variable requires declaration, that it is global - otherwise it is treated as local (and in case of referencing before assignement you get such errors).
Lets look at the code:
1. def ts_r(n):
2. for nn in [v for v in g[n] if not visited[v]]:
3. visited[nn] = 1
4. ts_r(nn)
5. f[n] = current_label
6. current_label -= 1
In line 5
you assign global variable value to f[n]
, but later, in line 6
you try to assign this global variable a value. You did not tell Python it is global, thus it assumes it is local. But if it is local, you cannnot assign it earlier.
You have two choices:
(probably not the one you are looking for) Use it as local:
def ts_r(n):
current_label = len(g) # initialize local variable
for nn in [v for v in g[n] if not visited[v]]:
visited[nn] = 1
ts_r(nn)
f[n] = current_label
current_label -= 1
Tell Python it is global variable and you would like to change global variable's value:
def ts_r(n):
global current_label # current_label is now global
for nn in [v for v in g[n] if not visited[v]]:
visited[nn] = 1
ts_r(nn)
f[n] = current_label
current_label -= 1
EDIT:
After your question was updated, I saw nested functions instead of functions defined in global scope. Thus the solution with global
won't work.
In Python 3.x you have nonlocal
keyword, but you will need to find walkaround in case of Python 2.x. Again, you have at least two possibilities:
Use mutable variable enclosing immutable you want to change (eg. list with one integer in it). Then when you just refer to (and change) the first element of a list. Try it.
Another solution is to add an attribute for the wrapping function (function is also a mutable, so you can change it, but you will not pollute global namespace). Example is here: http://ideone.com/7jGvM. In your case it may look like this:
def topological_sort(g):
visited = zeros((len(g)), dtype='int32')
f = zeros((len(g)), dtype='int32')
topological_sort.current_label = len(g) # [] so it is seen inside ts_r
def ts_r(n):
for nn in [v for v in g[n] if not visited[v]]:
visited[nn] = 1
ts_r(nn)
f[n] = topological_sort.current_label
topological_sort.current_label -= 1
for i in range(len(g)):
if not visited[i]:
ts_r(i)
return f
Upvotes: 3