Search code examples
pythonpython-3.xrecursionpass-by-value

Pass by value for recursion?


I am trying to pass a parameter "by value". I have tried making a deep copy of the parameter that is passed recursively in order to prevent any changes from circling back to the parent function calls.

Here is a snippet of code that tries to generate the array of all possible parentheses.

def generateParenthesis(n):
    #Iterate for each move.
    M = 2 * n
    retArray = []
    def recHelper(numMoves, perm, stack):
        print("Function call: ", numMoves, perm, stack)
        newPerm = copy.deepcopy(perm)
        newStack = stack.copy()
        #Base case, no moves
        if (numMoves == 0):
            retArray.append(newPerm)
            return

        #Case when left move is valid
        if (numMoves != len(newStack)):
            #Apply the left move. Pass it recursively
            newPerm +='('
            #Update the stack accordingly
            newStack.append('(')
            #Decrease numMoves
            newNumMoves = numMoves - 1
            #Call it recursively
            recHelper(newNumMoves, newPerm, newStack)
        #Case when right move is valid
        if len(newStack) != 0:
            #Apply the right move. Pass it recursively
            newPerm +=')'
            #Update the stack accordingly, delete the top, last elm
            newStack.pop()
            #Decrease numMoves
            newNumMoves = numMoves - 1
            #Call it recursively
            recHelper(newNumMoves, newPerm, newStack)
        #done
        return
    recHelper(M, "", [])
    return retArray

Unfortunately, calling generateParenthesis(1) returns ['()','()(', '()()'] and not ['()'].


Solution

  • def generateParenthesis(n):
        retArray = []
    
        def append_solutions(partial, opened_p, closed_p):
            if opened_p < n:
                append_solutions(partial + '(', opened_p + 1, closed_p)
            if closed_p < n and opened_p > closed_p:
                append_solutions(partial + ')', opened_p, closed_p + 1)
    
            if opened_p == closed_p == n and partial:
                retArray.append(partial)
    
        append_solutions('', 0, 0)
        return retArray
    
    print(generateParenthesis(1))
    print(generateParenthesis(2))
    print(generateParenthesis(3))
    print(generateParenthesis(4))
    print(generateParenthesis(5))
    

    After 3 hours of simplifying ideas, I came with this working code.

    Now it finds things like (()())() for generateParenthesis(4), for instance.

    The code is very self explanatory. You keep a count of opened/closed parenthesis and only close parenthesis when there are correspondent opened.

    I chose not to use a stack because since everything in Python is passed by "pointer copy", stacks (i.e. lists in your OP) would require a costly list(stack) in the function body, creating a local copy of the list.

    Notice that I create new strings (partial + '(', for instance), and these new string objects are passed by "pointer copy" to recursion calls.

    (Sorry I lack a better term now. But this is important)


    Edit

    Your solution has a problem with the variable newPerm. Its value is leaking into the second recursive function. Also, you need to understand that all your copying of values is not needed except for the stack.

    See how your function can be simplified. I think it's working:

    def generateParenthesisOP(n):
        #Iterate for each move.
        M = 2 * n
        retArray = []
        def recHelper(numMoves, perm, stack):
            print("Function call: ", numMoves, perm, stack)
            if numMoves == 0:
                retArray.append(perm)
                return
    
            if numMoves != len(stack):
                left_stack = list(stack)
                left_stack.append('(')
                recHelper(numMoves - 1, perm + '(', left_stack)
            if len(stack) != 0:
                right_stack = list(stack)
                right_stack.pop()
                recHelper(numMoves - 1, perm + ')', right_stack)
        recHelper(M, "", [])
        return retArray