0

Suppose I have a binary search tree [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]

If I run the following function, I want to know how many times the recursion function executes(in the following example, it is 31)

def loopBST(root): if not root: return loopBST(root.left) loopBST(root.right) 

I can get this by create a global variable

global ind ind = 0 def loopBST(root): global ind ind += 1 if not root: return loopBST(root.left) loopBST(root.right) loopBST(bsttree) 

The variable ind will be 31.

The question is, how can I make this indinside the dfs function rather than creating the global variable?

3
  • 1
    Can you please use codeblock formatting? Do four spaces of indentation for every line you have code and it will look a lot cleaner. Commented Feb 8, 2018 at 22:26
  • Possible duplicate of Counting python method calls within another method Commented Feb 8, 2018 at 23:03
  • Counting function calls Commented Feb 8, 2018 at 23:06

4 Answers 4

6

You could return the number of executions:

def loopBST(root): if not root: return 1 return 1 + loopBST(root.left) + loopBST(root.right) 
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks Stefan. I see you from leetcode. In fact this question is from the question I am practicing in leetcode. I know I can solve this by put the dfs inside a class and set up a global count variable inside the class to finish this job. I am wondering if there is any way I can put that count variable as a parameter of the dfs function. Can I do this in the recursion fuction? I can create a dfs parameter called temp list and append a value to that temp list once there is a recursion run.
Which question is it there? I don't think leetcode ever asked to count function calls...
for example, q230. to return kth smallest of bst. You have fancy way to solve it. I try to start from stupid way: goes to the left lowest leaf, and then goes up, if I goes up k(my i==k), I will get the answer. My stupid code is like def kthsmall2(root, k): if not root: return def dfs(node, i, k): i += 1 if not node: return dfs(node.left, i, k) if i == k: return node.val dfs(node.right, i, k) return i print dfs(root, 0, k)
5

You could use a parameter.

def loopBST(root, times=0): times += 1 if not root: return times times = loopBST(root.left, times=times) times = loopBST(root.right, times=times) return times loopBST(bsttree) 

2 Comments

No problem! I missed something too; I forgot to return times at the end of the function. Edited.
Oh, good, so I was somewhat right after all :-P. I wish they had provided usable testing data instead of an unclear list, then none of this would've happened...
2

If you don't want to rewrite your recursive function, you could also decorate it in a function that counts it using a counter of some sort. Example of an implementation:

UPDATE: I've changed the answer, but the old answer is kept at the end of the answer //UPDATE

Assume here that you have some recursion functions in some_module.py:

# From some_module.py def factorial(x): return factorial(x-1)*x if x > 1 else 1 def cumsum(x): return cumsum(x-1) + x if x > 1 else 1 def loopBST(root): # ... your code 

And you want to apply the decorator to count how many recursions ran. Here, the code is performed inside some_function() to show that you don't have to keep the count variable(s) in the global scope. (See comments) (Also, the recursive functions are still in global space)

# Main file: from functools import wraps, partial from collections import defaultdict # import the two recursion functions from some_module.py from some_module import cumsum, factorial, loopBST def some_function(): global factorial, cumsum, loopBST def counting_recursion(fn, counter): @wraps(fn) def wrapper(*args, **kwargs): counter[fn.__name__] += 1 return fn(*args, **kwargs) return wrapper counters = defaultdict(int) my_deco = partial(counting_recursion, counter=counters) factorial = my_deco(factorial) cumsum = my_deco(cumsum) loopBST = my_deco(loopBST) print(factorial(3)) # 6 print(cumsum(5)) # 15 factorial_count = counters[factorial.__name__] cumsum_count = counters[cumsum.__name__] loopBST_count = counters[loopBST.__name__] # Equals 0: were not called in my example print('The "{}" function ran {} times'.format(factorial.__name__, factorial_count)) print('The "{}" function ran {} times'.format(cumsum.__name__, cumsum_count)) # The "factorial" function ran 3 times # The "cumsum" function ran 5 times 

A few modifications/variations:

Instead of using my_deco = partial(counting_recursion, counter=counters), the recursive functions could be decorated directly:

cumsum = counting_recursion(cumsum, counter=counters) factorial = counting_recursion(factorial, counter=counters) loopBST = counting_recursion(loopBST, counter=counters) 

Instead of using fn.__name__ to identify the called function, the counting_recursion-function could be rewritten as:

def counting_recursion(fn, counter): @wraps(fn) def wrapper(*args, **kwargs): counter[wrapper] += 1 return fn(*args, **kwargs) return wrapper 

Then, to read the number from the counters dictionary:

factorial_count = counters[factorial] cumsum_count = counters[cumsum] loopBST_count = counters[loopBST] 

If you want to read more about wrapping functions, check out https://stackoverflow.com/a/25827070/1144382 and the docs on wraps

OLD EXAMPLE:

from functools import wraps, partial class Counter: def __init__(self, start_count=0): self._counter = start_count def __repr__(self): return str(self._counter) def count(self): self._counter += 1 counter = Counter() def counting_recursion(fn, counter): @wraps(fn) def wrapper(*args, **kwargs): counter.count() return fn(*args, **kwargs) return wrapper my_deco = partial(counting_recursion, counter=counter) @my_deco def factorial(x): return factorial(x-1)*x if x > 1 else 1 print(factorial(3)) # 6 print('The {} function ran {} times'.format(factorial.__name__, counter)) # The factorial function ran 3 times 

To implement this in your case, just make some counter and decorate your function:

@my_deco def loopBST(root): # ... print(counter._counter) # prints number of counts 

Of course, you don't have to make a Counter-class to call counter.count() on, you could also have a dictionary of counters, e.g. counts[loopBST] += 1 or just an array with a single element count_list[0] += 1. (see the code example at top of this answer) (The entire point is to "hide" the value in a reference that is not rewritten when the variable is reassigned, which is why just an integer count count += 1 won't work.)

2 Comments

I like this solution with decorator. It might be a good idea to modify the decorator so that we can avoid the global variable for the empty dictionary and may be importing the partial function
I see, thanks for follow up, I will update my answer when I get back to my computer.
-1

Maybe you can try with instance of a class, this way it probably is cleaner than having keyword argument. Not to mention that you can store additional data in instance.

This is a Python 2.7 class.

class LoopBST(object): def __init__(self): self.ind = 0 def __call__(self, root): self.ind += 1 if not root: return self(root.left) self(root.right) loopBST = LoopBST() loopBST(bstree) print loopBST.ind 

1 Comment

Encapsuling such simple task like this inside a class doesn't seem to be a very pythonic way.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.