9

I am brushing up a bit of good old algorithms, and doing it with python, since I use it more often nowadays.

I am facing an issue when running a recursive function; where the variable get reset every time that the recursive function call itself:

def recursive_me(mystring):
    chars = len(mystring)
    if chars is 0:
        print("Done")
    else:
        first = int(str[0])
        total = + first
        print(total)
        recursive_me(mystring[1:])

recursive_me("4567")

What I am doing here is to get a string made of digits; take the first, convert it to an int; and run recursively the function again, so I can take one digit at time from the string and sum all the values.

Ideally the output should show the total, while it add all the digits (4+5+6+7), although when the recursive function is called the first time, the function reset the total value.

Is common habit to use global variables when running operations with recursive functions or am I doing something wrong?

  • 4
    note that `=+` != `+=`. – timgeb Dec 29 '15 at 22:10
  • also that you're doing `str[0]` where str is a python built-in class and it should be actually `mystring[0]` I believe. At the same time it's very unusual to ever use global variables (although sometimes justified) and you aren't using any global var now. – ljetibo Dec 29 '15 at 22:12
  • By the way, you want to test `if chars == 0` instead of using the `is` keyword. `is` compares the *object identifiers*, not their *values*. See here: [Understanding Python's "is" operator](http://stackoverflow.com/a/13650309/4467665) – thatWiseGuy Dec 29 '15 at 22:14
  • 1
    I think his issue is that since he's not using a global var, it's getting reset – Untitled123 Dec 29 '15 at 22:14
  • 3
    @Untitled123; to ever *need* a global variable is strange. To use it here is strange as well. He doesn't need it. – ljetibo Dec 29 '15 at 22:15
  • i guess what i mean is that the most minimal fix here is to make total global, not that I would ever suggest doing so. – Untitled123 Dec 29 '15 at 22:17
  • @Untitled123 Why go for a "minimal fix" when a clean solution is nicer and more concise than the code provided? – pjs Dec 29 '15 at 22:20
  • 1
    I would say that when you're learning how to do something, skipping right to the answer is not as instructive as progressively building up working solutions so you understand the reasoning of fixes (whether for maintenance, speed, readability etc). If you want to be concise, sum(map(int,mystring)) would be possibly the shortest. Since he wanted a recursive solution, we'll oblige, but there's really no reason for that here. – Untitled123 Dec 29 '15 at 22:23

5 Answers5

10

You can code as simply as this:

def recursive_me(mystring):
    if mystring: # recursive case
        return int(mystring[0]) + recursive_me(mystring[1:])
    else:        # base case
        return 0

or

def recursive_me(mystring, total = 0):
    if mystring: # recursive case
        return recursive_me(mystring[1:], total + int(mystring[0]))
    else:        # base case
        return total

although this won't help much in Python since it doesn't implement tail-call optimisation.

If you want to see the intermediate values, change the second version like so:

def recursive_me(mystring, total = 0):
    if mystring: # recursive case
        newtotal = total + int(mystring[0])
        print(newtotal)
        return recursive_me(mystring[1:], newtotal)
    else:        # base case
        return total

then

4
9
15
22
22 # this is the return value; previous output is from `print()`
uselpa
  • 18,732
  • 2
  • 34
  • 52
  • It might be instructive to show a recursive solution where it divides the string by half each time. – Untitled123 Dec 29 '15 at 22:25
  • @Untitled123 I don't see why you'd want to do that but please feel free to add your own answer, maybe it'll become clearer. – uselpa Dec 29 '15 at 22:27
  • I guess what I mean is that this recursive solution has the exact same call stack as an iterative solution. – Untitled123 Dec 29 '15 at 22:29
  • 2
    @Untitled123 The iterative solution has no call stack whatsoever since it doesn't call itself. Of course it is not natural to write such a code in Python, but it is in other languages such as Scheme where the second solution is the normal way to iterate. – uselpa Dec 29 '15 at 22:31
  • 1
    fair enough :) (more than 15 chars) – Untitled123 Dec 29 '15 at 22:32
  • 1
    Thanks for the answer! –  Dec 30 '15 at 04:43
3

as a foreword: a lot of answers received meaningful edits in the meantime I was writing this answer. Don't hold it against me.

I'm throwing my two cents in here just because there's a lot of over-complicated answers. This is a corrected copy-paste of the OP's effort.

def recursive_me(mystring, total=0):
    chars = len(mystring)
    if chars is 0:
        print("Done")
        return total
    else:
        first = int(mystring[0])
        total += first
        print(total)
        recursive_me(mystring[1:], total)

first what happens is that we check the base case, if there's no left chars in the string. If the string length is 0 we return the total calculated ammount.

Otherwise, we turn the first of the chars into an int, and add it to total. The first error you have is that you wrote str[0]. str is a python built in type and the produced error would be something like "str is not subscriptable".

This error means that the str can't be operated on by "[]" operator. The same would happen if you tried doing 1[0] because 1 is a integer. The "[]" operator can only operate on lists, tuples and strings (I might've forgot some built-in type).

The second error you had was with the addition part. You had written total = + first but the operator you are looking for is the += which in fact is just a shortened way to write a = a+b.

Additionally, your original question was concerning about "python" forgetting the value of "total". This is because you have to either pass that value forward, or write your recursive function in a way that "forces" it to, what's called, evaluate your next call to your function on the spot.

In my example I'm sending the next call of the function recursive_me, the current total value. In the example given by @uselpa; above he's making python evaluate the next call to the function by putting it after operator +:

return int(mystring[0]) + recursive_me(mystring[1:])

this then gets to be (for recursive_me("4567"))

return int(4)+recursive_me("567")
return int(4)+int(5)+recursive_me("67")
....
return int(4)+int(5)+int(6)+int(7)+0

because python needs to return a value here, but the expression keeps calling new functions and python can't return until it evaluates all of them to a final number (in this case at least).

ljetibo
  • 3,048
  • 19
  • 25
  • `return int(4)+int(5)+int(6)+int(7)+0` (don't forget to add 0) – uselpa Dec 29 '15 at 22:38
  • Thanks for the answer!, I did realize that I wrote the code wrong; the += was a total no brainer; but the str I blame it on the intellisense of pycharm...can't believe that I didn't see it. –  Dec 30 '15 at 04:49
  • @newbiez; np, it's important to understand that there's no magic behind how Python gets the final result, you either have to remember and send the newly calculated value back to the function when you call it again, or force python to keep evaluating the expression until it reaches the end (in this case the previous values are kept "on the stack". In this 2nd case it might seem a bit like there's something that auto-remembers the previous value, but there's not. – ljetibo Dec 30 '15 at 11:50
1

The common practice is to save these variables as parameters, and pass them along the chain. It seems in your case, you would want to pass total as an additional parameter, and update it as needed.

There's also a neat functional way to do it in python

t=raw_input()
print reduce(lambda a, b: a+b, map(int,t))

This is recursive in nature.

Untitled123
  • 1,317
  • 7
  • 20
  • 4
    Not really, the common practice is to have your recursive function return something. – pjs Dec 29 '15 at 22:18
  • Haha ,that's true. I guess what I meant was in cases where you want status variables that change. The case I had in mind was a DFS for example (where one would need to keep track of the current position via parameters) – Untitled123 Dec 29 '15 at 22:19
  • `reduce` is not really natural here. `sum` would be much better. You also don't need `list(t)`, since strings are already iterable. – Blckknght Dec 29 '15 at 22:22
  • Was trying to force it into a recursive state, is sum recursive? – Untitled123 Dec 29 '15 at 22:24
0

Some pointers:

  • Your default case should return an actual number (0 in your case) and not just print done.
  • total = + first is setting total to first, not adding first to total. You would need total += first to do the latter.
  • The trick with "retaining" the value of your current total is to "save" it in the recursive call-chain itself by passing it along with each call. You won't need a global variable or a default parameter to do this.

Here's a solution:

def recursive_me(mystring):
    if not mystring: # True if mystring is empty
        return 0
    return int(mystring[0]) + recursive_me(mystring[1:])

print(recursive_me("4567")) # 22
timgeb
  • 76,762
  • 20
  • 123
  • 145
0

Here is a solution that uses the LEGB scope rule to avoid creating a new string instance on every recursive call

def sum_str(mystring):
    def recursive_me(pos):
        cur_char = int(mystring[pos])
        if pos:
            return cur_char + recursive_me(pos-1)
        else:
            return cur_char
    return recursive_me(len(mystring)-1)

s = '4567'
print('summing', s)
print(sum_str(s))

However, indexing can be avoided as well by iterating on the string

def sum_str(mystring):
    def recursive_me(itx):
        try:
            cur_char = int(next(itx))
            return cur_char + recursive_me(itx)
        except StopIteration:
            return 0

    return recursive_me(iter(mystring))

Obviously, both solutions produce

summing 4567
22
Pynchia
  • 10,996
  • 5
  • 34
  • 43