0

I have a problem where running my defined functions with same arguments gets different results depending on how I call the function with it's default values.

I have a function theEnd(n, C) that calculates the sum of some recursively defined function testFunc. testFunc tracks it's previously done calculations in a dictionary computed.

testFunc is defined as:

def testFunc(var, loopVar, param, computed = {(0,0) : 1, (0,1) : 1}):
    """ Does some recursive calculation for loopVar 
        testFunc(loopVar, var, ...) = sum over var: var * testFunc(loopVar - 1, var, ...)
        sum goes from 0 <= var < param
        tracks all previously done calculations in computed
    """
    # basecase f(0,0) = 1 and f(0,x) = 1
    if var == 0 and loopVar > 0:
        computed[(var, loopVar)] = 1
    # if not in computed do the recursive implementation and save to computed
    if (var, loopVar) not in computed.keys():
        total = 0
        for i in range(param):
            total += i * testFunc(var-1, i, param, computed)
        computed[(var, loopVar)] = total
    return computed[(var, loopVar)]  

The problem depending on how I define my theEnd function I will get inconsistent results. I am sure it has to do with not calling the default values for the computed dictionary, but why does my theEnd require to call my testFunc redefining the default computed dictionary?

Option 1 is defining it to sum over testFunc(n, i, C) without calling computed = {(0,0) : 1, (0,1): 1}.

Option 2 is defining it to sum over testFunc(n, i, C, computed = {(0,0) : 1, (0,1): 1} )

Option 1 outputs for n = 3, C = 3 and n = 4 and C = 4

(n, C) = (3, 3) : 81
(n, C) = (4, 4) : 1620

Option 2 outputs for the same values

(n, C) = (3, 3) : 81
(n, C) = (4, 4) : 5184

Code for the two different options:

OPTION 1

def theEnd(n, C):
    """ computes final results by calculating
        thing = sum over i: testFunc(n, i, C)
        with 0 <= i < C
    """
    thing = 0
    for i in range(C):
        thing += testFunc(n, i, C)
    return thing             

    print("(n, C) =", (3,3), ":", theEnd(3,3))            
    print("(n, C) =", (4,4), ":", theEnd(4,4))

OPTION 2

def theEnd(n, C):
    """ computes final results by calculating
        thing = sum over i: testFunc(n, i, C)     with 0 <= i < C
    """
    thing = 0
    for i in range(C):
        thing += testFunc(n, i, C, computed = {(0,0) : 1, (0,1) : 1})
    return thing             

    print("(n, C) =", (3,3), ":", theEnd(3,3))            
    print("(n, C) =", (4,4), ":", theEnd(4,4))
jdehesa
  • 58,456
  • 7
  • 77
  • 121
Gehaktmolen
  • 171
  • 4
  • @tobias_k checking for presence of (var, loopVar) in computed hints, means that the who wrote this code was aware of the fact and uses it to cache computations. – Konstantin Oznobihin Jul 12 '19 at 12:47
  • the issue with this code is that computed caches computation results only for var and loopVar parameters while ignoring the 'param' one. It seems like you'll need a root function with (var, loopVar, param) parameters which will call testFunc with new computed so that computations for different param values wouldn't interfere with each other. – Konstantin Oznobihin Jul 12 '19 at 12:52
  • @KonstantinOznobihin Still, the problem seems to be that OP misunderstood when `computed` will be reused and when not. There are many ways to fix it. Another would be to use `@functools.lru_cache` instead, or use `computed = None` as default and then `computed = computed or {(0,0) : 1, (0,1) : 1}` in the first line of the function so that the dict does not have to be passed as a parameter. – tobias_k Jul 12 '19 at 14:12
  • Thanks for all the comments, I wasn't aware that default values were defined on function definition instead of call! – Gehaktmolen Jul 16 '19 at 11:38

0 Answers0