Home > Software engineering >  Conceptual understanding of variable scope in higher order functions
Conceptual understanding of variable scope in higher order functions

Time:12-12

I have a function score0 which gives me a score for every turn (for n turns) in a loop. This score is incremented by a random integer from 1 to 15 every turn.

I now have to design another higher order function which should print the highest score jump of a player out of all the score jumps yet, and should be called inside the score0 function. I name it highest_gain. Naturally, this should print the first score value as it's the first turn (hence it is the biggest jump).

# Function that defines the highest point jump in a score yet
import random
def highest_gain(previous_value, highest_point):
    def say(score) :
        if previous_value == 0:
            print ('Biggest gain by player0 yet with',score,'points!') 
            return highest_gain(score, score) 
        gain = score - previous_value
        if gain > highest_point:
           print('Biggest gain by player0 yet with',score,'points!') 
           return highest_gain(score, gain)
    return say

# Function that gives me a new score (incremented) every turn    
def score0(n, score = 0):
    while n > 0:
          score  = random.randint(1, 15)
          highest_gain(previous_value = 0,highest_point = 0)(score)
          n -= 1
    return score

#Calling the function   
score0(4,0)

Python Tutor link

The problem is that calling highest_gain() doesn't update the values of previous_value and highest_point. Why aren't these variables getting updated in the score0() function body, and how should highest_gain() be called be called so that these variables are updated on each iteration of the loop?

CodePudding user response:

Your highest_gain function is a higher-order function which returns another function named say. When say is called, it calls highest_gain again and returns the result, which is - again - the function say. The important point here is that say is a closure over the local variables of the outer function highest_gain, so each time highest_gain is called you get a different instance of the say function, with different values of the outer function's local variables.

Now, since calling say returns another instance of say which closes over the updated values, that means you need to keep the result from when you call it, so you can call the new instance which closes over those updated values.

def score0(n, score=0):
    say = highest_gain(previous_value=0, highest_point=0)
    while n > 0:
          score  = random.randint(1, 15)
          say = say(score) or say
          n -= 1
    return score

I moved the original call of highest_gain to before the loop, since you don't want to use the initial values of 0 on every iteration.

Note that say doesn't always return a new instance of say - sometimes it returns None, so I used the trick say(score) or say here so that say keeps its old value if there isn't a new one to update it with. You could alternatively write this as below, which is more verbose but perhaps makes clearer what this is doing:

new_say = say(score)
if new_say is not None:
    say = new_say

Otherwise, you could change the definition of say so that it returns itself (i.e. with the current values of the variables from the outer function) when there should be no update, and then in score0 you could just write say = say(score).

  • Related