Search code examples
python-3.xuppercaselowercase

Getting string index out of range? python 3


My program is supposed to take an input in form of a string and split into to strings, one with all the lower case letters, underscores and dots. The other one with all the upper cases, the pipes and the spaces. I am not supposed to use (for function)

def split_rec (letters):
    uppers = ""
    lowers = ""
    if letters[0].isupper() or letters[0] == "|" or letters[0].isspace():
        uppers += letters[0] + split_rec (letters[1:])
    elif letters[0].islower() or letters[0] == "_" or letters[0] == ".":
        lowers += letters[0] + split_rec (letters[1:])
    elif not letters:
        return lowers, uppers

Can you please tell me more about the error I'm getting?

if letters[0].isupper() or letters[0] == "|" or letters[0].isspace(): IndexError: string index out of range


Solution

  • You have defined a recursive function: the split_rec calls split_rec (letters[1:]). Since a string has finite length, eventually you will pass it the empty string. So that means that you can not access the first character, since it has no first character.

    You can solve that problem by using a guard:

    def split_rec (letters):
        uppers = ""
        lowers = ""
        if not letters:
            return lowers, uppers
        if letters[0].isupper() or letters[0] == "|" or letters[0].isspace():
            uppers += letters[0] + split_rec (letters[1:])
        elif letters[0].islower() or letters[0] == "_" or letters[0] == ".":
            lowers += letters[0] + split_rec (letters[1:])
        elif not letters:
            return lowers, uppers

    But this will not solve the problem: since split_rec returns a tuple of two strings, you can not add this to a character. You can solve the problem by using:

    def split_rec (letters):
        if not letters:
            return lowers, uppers
        lowers, uppers = split_rec(letters[1:])
        if letters[0].isupper() or letters[0] == "|" or letters[0].isspace():
            uppers = letters[0] + uppers
        elif letters[0].islower() or letters[0] == "_" or letters[0] == ".":
            lowers = letters[0] + lowers
        return lowers, uppers

    But nevertheless recursion that scales linear with input is in Python a bad idea: Python does not optimize recursive calls, and so one easily gets a StackOverflow exception (this has nothing to do with this site).

    You better use a generator and join the strings together:

    def split_rec (letters):
        uppers = ''.join(c for c in letters if c.isupper() or c == "|" or c.isspace())
        lowers = ''.join(c for c in letters if c.islower() or c == "_" or c == ".")
        return lowers, uppers