I found a solution to Leetcode NO. 543 Diameter of Binary Tree which is solved with the use of global variables. I have the solution shown in the text below. The author used a scalar variable, 'self.res', to store the final answer which is updated when the program traverses through the given binary tree.
I am wondering why the author needs to use self.res rather than a generic integer variable---for example, res only---to store the answer; when I replace self.res with res, the answer is wrong. Can anyone point out the difference?
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
self.res = 0
def depth(root):
if not root:
return 0
left = depth(root.left)
right = depth(root.right)
self.res = max(self.res, left + right)
return max(left, right) + 1
depth(root)
return self.res
Strictly speaking, it doesn't have to be an attribute. It could be a non-local variable.
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
res = 0
def depth(root):
nonlocal res
if not root:
return 0
left = depth(root.left)
right = depth(root.right)
res = max(res, left + right) # the key to memorize the result
return max(left, right) + 1
depth(root)
return res
Without the nonlocal statement, res would be a local variable that isn't set the first time depth tries to use it in the call to max.
Python 2 did not have a nonlocal statement, so an instance attribute would have been the best alternative to a global variable for persisting state between calls to depth. (The type hints indicate this probably wasn't written for Python 2, but old habits can die hard.) In Python 3, using nonlocal, the above no longer even uses self, so diameterOfBinaryTree can trivially be written as a regular function rather than an instance method of an unnecessary class.
Related
I have just almost finished my assignment and now the only thing I have left is to define the tostring method shown here.
import math
class RegularPolygon:
def __init__(self, n = 1, l = 1):
self.__n = n
self.__l = l
def set_n(self, n):
self.__n = n
def get_n(self):
return self.__n
def addSides(self, x):
self.__n = self.__n + x
def setLength(self, l ):
self.__l = l
def getLength(self):
return self.__l
def setPerimeter(self):
return (self.__n * self.__l )
def getArea(self):
return (self.__l ** 2 / 4 * math.tan(math.radians(180/self.__n)))
def toString(self):
return
x = 3
demo_object = RegularPolygon (3, 1)
print(demo_object.get_n() , demo_object.getLength())
demo_object.addSides(x)
print(demo_object.get_n(), demo_object.getLength())
print(demo_object.getArea())
print(demo_object.setPerimeter())
Basically the tostring on what it does is return a string that has the values of the internal variables included in it. I also need help on the getArea portion too.
Assignment instructions
The assignment says
... printing a string representation of a RegularPolygon object.
So I would expect you get to choose a suitable "representation". You could go for something like this:
return f'{self.__n+2} sided regular polygon of side length {self.__l}'
or as suggested by #Roy Cohen
return f'{self.__class__.__name__}({self.__n}, {self.__l})'
However, as #Klaus D. wrote in the comments, Python is not Java, and as such has its own standards and magic methods to use instead.
I would recommend reading this answer for an explanation between the differences between the two built-in string representation magic-methods: __repr__ and __str__. By implementing these methods, they will automatically be called whenever using print() or something similar, instead of you calling .toString() every time.
Now to address the getters and setters. Typically in Python you avoid these and prefer using properties instead. See this answer for more information, but to summarise you either directly use an objects properties, or use the #property decorator to turn a method into a property.
Edit
Your area formula is likely an error with order-of-operations. Make sure you are explicit with which operation you're performing first:
return self.__l ** 2 / (4 * math.tan(math.radians(180/self.__n)) )
This may be correct :)
I was doing some leetcode problems and found that I couldn't carry my variables through recursive functions as I thought I could.
So for example, say I wanted to sum all of nodes of a tree.
So I thought of implementing it this way:
def Sum(root):
def dfs(root,x):
if root:
dfs(root.left, x)
x.append(root.val)
dfs(root.right,x)
return x
return(sum(dfs(root,x=[])))
And this works. However, say I want to cut down on memory, how come this implementation doesn't work and just returns the root node.
def Sum(root):
def dfs(root,x):
if root:
dfs(root.left, x)
x+=(root.val)
dfs(root.right,x)
return x
return(sum(dfs(root,x=0)))
Any help or guidance would be appreciated.
x is mutable in your first definition; each call to dfs gets a reference to the same list.
In your second example, x is immutable. The value of x is not modified by the recursive call; x += root.val just updates the local copy of the argument.
Instead, you need to add the return value of dfs directly to x.
def Sum(root):
def dfs(root, x):
if root:
x += dfs(root.left, x)
x += root.val
x += dfs(root.right,x)
return x
return dfs(root, x=0)
There's no need to define dfs, since you aren't really doing a generic search or walk any more. Just call Sum recursively. Also, the return value is sufficient; you don't need an x argument at all.
def Sum(root):
if not root:
return 0
else:
return Sum(root.left) + root.val + Sum(root.right)
I have the following class:
class Node():
def __init__(self, symbol, rule, children):
self.symbol = symbol
self.rule = rule
self.children = children
def addChild(self,child):
self.children.append(child)
I use it to build parsing trees; now I'm trying to use this function:
def simplify(n):
if len(n.children) == 0:
return n
if len(n.children) > 1:
for c in n.children:
c = simplify(c)
return n
while len(n.children) == 1:
print n.symbol,
n = n.children[0] #What is wrong here?
print n.symbol
return n
to simplify trees by removing internal nodes that have just one child. For instance:
S S
/ \ should become / \
E X v X
/
v
When I run the code, the two print statements show me that n was correctly replaced by n.children[0] , but when the tree is printed (right after this funtion is used), I see the same one. What's the problem here?
In your simplify() function, the parameter n is a reference to some specific node, and you can change what node it refers to; but reassigning n doesn't change any of the other structure. As a specific example, this loop actually does nothing:
for c in n.children:
# simplify has no side effects and leaves the input structure unchanged
c = simplify(c)
# c is never used again so the simplified result is lost
There's two reasonable approaches to solving this. One is to construct a new tree as the result of simplify:
def simplify(n):
if len(n.children) > 1:
new_children = [simplify(c) for c in n.children]
return Node(n.symbol, n.rule, new_children)
# and other cases
This has the advantage that your data structure is immutable: if you have two references to the tree hanging around, you know that rewriting one isn't going to destroy the other; if you have a reference to a node in the middle of the tree, there's no risk of it unexpectedly becoming "orphaned".
Still, it is common enough to see mutable data structures, and you could add your simplify method into the Node class to rewrite a node in place:
class Node:
def simplify(self):
if len(self.children) == 1:
return self.children[0].simplify()
if len(self.children) > 1:
self.children = [c.simplify() for c in self.children]
return self
I am reading Hackers and Painters and am confused by a problem mentioned by the author to illustrate the power of different programming languages.
The problem is:
We want to write a function that generates accumulators—a function that takes a number n, and returns a function that takes another number i and returns n incremented by i. (That’s incremented by, not plus. An accumulator has to accumulate.)
The author mentions several solutions with different programming languages. For example, Common Lisp:
(defun foo (n)
(lambda (i) (incf n i)))
and JavaScript:
function foo(n) { return function (i) { return n += i } }
However, when it comes to Python, the following codes do not work:
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
f = foo(0)
f(1) # UnboundLocalError: local variable 's' referenced before assignment
A simple modification will make it work:
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
I am new to Python. Why doesn the first solution not work while the second one does? The author mentions lexical variables but I still don't get it.
s += i is just sugar for s = s + i.*
This means you assign a new value to the variable s (instead of mutating it in place). When you assign to a variable, Python assumes it is local to the function. However, before assigning it needs to evaluate s + i, but s is local and still unassigned -> Error.
In the second case s[0] += i you never assign to s directly, but only ever access an item from s. So Python can clearly see that it is not a local variable and goes looking for it in the outer scope.
Finally, a nicer alternative (in Python 3) is to explicitly tell it that s is not a local variable:
def foo(n):
s = n
def bar(i):
nonlocal s
s += i
return s
return bar
(There is actually no need for s - you could simply use n instead inside bar.)
*The situation is slightly more complex, but the important issue is that computation and assignment are performed in two separate steps.
An infinite generator is one implementation. You can call __next__ on a generator instance to extract successive results iteratively.
def incrementer(n, i):
while True:
n += i
yield n
g = incrementer(2, 5)
print(g.__next__()) # 7
print(g.__next__()) # 12
print(g.__next__()) # 17
If you need a flexible incrementer, one possibility is an object-oriented approach:
class Inc(object):
def __init__(self, n=0):
self.n = n
def incrementer(self, i):
self.n += i
return self.n
g = Inc(2)
g.incrementer(5) # 7
g.incrementer(3) # 10
g.incrementer(7) # 17
In Python if we use a variable and pass it to a function then it will be Call by Value whatever changes you make to the variable it will not be reflected to the original variable.
But when you use a list instead of a variable then the changes that you make to the list in the functions are reflected in the original List outside the function so this is called call by reference.
And this is the reason for the second option does work and the first option doesn't.
Below is a simple piece of code I found in this tutorial.
Here's a nice definition of Closure I found here: "a function object that remembers values in enclosing scopes regardless of whether those scopes are still present in memory."
I gather that rotate() below is a closure. Please help me understand what values is it remembering even after their scope is gone from memory (and why does their scope leave memory)?
def make_rotater(seq):
def rotate():
val = seq.pop(0)
seq.append(val)
return val
return rotate
r = make_rotater([1,2,3])
r()
# 1
r()
# 2
(Update) Part 2: Why does the (closure-less) code below not work?
def make_rotater(seq):
val = seq.pop(0)
seq.append(val)
return val
r = make_rotater([1,2,3])
r()
# File "<stdin>", line 1, in <module>
# TypeError: 'int' object is not callable
It remembers local values from make_rotator so if u do:
def make_rotater():
seq=[1,2,3]
def rotate():
val = seq.pop(0)
seq.append(val)
return val
return rotate
The seq is referenced by rotate, so it will remain in memory for when you will call rotate, even though it was defined in make_rotater (that is already done, and cleaned from memory)
When you call make_rotater it creates a new seq and defines the rotate method that references seq, so one you leave make_rotater it's memory isn't needed except seq, (cause rotate still uses it). When you will no longer reference rotate, seq will also be cleaned
part 2:
your method now doesnt return another method it returns the number directly, so you dont need to do r()
you can use it like this:
seq = [1,2,3]
def make_rotater(seq):
val = seq.pop(0)
seq.append(val)
return val
r = make_rotater(seq)
print r # prints 1, note there is no r() just r
r = make_rotater(seq)
print r # prints 2
r = make_rotater(seq)
print r # prints 3
That definition is sort of right, sort of wrong. It depends on what you mean by a scope being "still present in memory". I'd say a better definition would be "a function object that remembers variables in enclosing scopes regardless of whether those scopes are still present on the call stack."
When you call make_rotater:
def make_rotater(seq):
def rotate():
val = seq.pop(0)
seq.append(val)
return val
return rotate
The rotate closure keeps the seq variable alive even after execution leaves the scope of make_rotater. Ordinarily, when execution leaves a function, its local variables would cease to exist.