Organizing strings in recursive functions - python - python

I'm trying to split and organize a string in a single function, my goal is to seperate lowercase and uppercase characters and then return a new string essentially like so:
"lowercasestring" + " " + "uppercasestring".
Importantly all characters must return in the order they were recieved but split up.
My problem is that i have to do this recursively in a single function(for educational purposes) and i struggle to understand how this is doable without an external function calling the recursive and then modifying the string.
def split_rec(string):
if string == '':
return "-" #used to seperate late
elif str.islower(string[0]) or string[0] == "_" or string[0] == ".": #case1
return string[0] + split_rec(string[1:])
elif str.isupper(string[0]) or string[0] == " " or string[0] == "|": #case2
return split_rec(string[1:]) + string[0]
else: #discard other
return split_rec(string[1:])
def call_split_rec(string):
##Essentially i want to integrate the functionality of this whole function into the recursion
mystring = split_rec(string)
left, right = mystring.split("-")
switch_right = right[::1]
print(left + " " + switchright)
The recursion alone would return:
"lowerUPPERcaseCASE" -> "lowercase" + "ESACREPPU"
My best attempt at solving this in a single function was to make case2:
elif str.isupper(string[-1]) or string[-1] == " " or string[-1] == "|": #case2
return split_rec(string[:-1]) + string[-1]
So that the uppercase letters would be added with last letter first, in order to correctly print the string. The issue here is that i obviously just get stuck when the first character is uppercase and the last one is lowercase.
I've spent alot of time trying to figure out a good solution to this, but im unable and there's no help for me to be found. I hope the question is not too stupid - if so feel free to remove it. Thanks!

I wouldn't do this recursively, but I guess you don't have a choice here. ;)
The simple way to do this in one function is to use a couple of extra arguments to act as temporary storage for the lower and upper case chars.
def split_rec(s, lo='', up=''):
''' Recursively split s into lower and upper case parts '''
# Handle the base case: s is the empty string
if not s:
return lo + ' ' + up
#Otherwise, append the leading char of s
# to the appropriate destination...
c = s[0]
if c.islower():
lo += c
else:
up += c
# ... and recurse
return split_rec(s[1:], lo, up)
# Test
print(split_rec("lowerUPPERcaseCASE"))
output
lowercase UPPERCASE
I have a couple of comments about your code.
It's not a great idea to use string as a variable name, since that's the name of a standard module. It won't hurt anything, unless you want to import that module, but it's still potentially confusing to people reading your code. The string module doesn't get a lot of use these days, but in the early versions of Python the standard string functions lived there. But then the str type inherited those functions as methods, making the old string functions obsolete.
And on that note, you generally should call those str methods as methods, rather than as functions. So don't do:
str.islower(s[0])
instead, do
s[0].islower()

Another take with recursive helper functions
def f(s):
def lower(s):
if not s:
return ''
c = s[0] if s[0].islower() else ''
return c + lower(s[1:])
def upper(s):
if not s:
return ''
c = s[0] if s[0].isupper() else ''
return c + upper(s[1:])
return lower(s) + ' ' + upper(s)

The easiest way would be to use sorted with a custom key:
>>> ''.join(sorted("lowerUPPERcaseCASE" + " ", key=str.isupper))
'lowercase UPPERCASE'
There's really no reason to use any recursive function here. If it's for educational purpose, you could try to find a problem for which it's actually a good idea to write a recursive function (fibonacci, tree parsing, merge sort, ...).
As mentioned by #PM2Ring in the comments, this sort works fine here because Python sorted is stable: when sorting by case, letters with the same case stay at the same place relative to one another.

Here is a way to do it with only the string as parameter:
def split_rec(s):
if not '|' in s:
s = s + '|'
if s.startswith('|'):
return s.replace('|', ' ')
elif s[0].islower():
return s[0] + split_rec(s[1:])
elif s[0].isupper():
# we move the uppercase first letter to the end
s = s[1:] + s[0]
return split_rec(s)
else:
return split_rec(s[1:])
split_rec('aAbBCD')
# 'ab ABCD'
The idea is:
We add a marker at the end (I chose |)
If the first char is lowercase, we return it + the organized rest
If it is uppercase, we move it to the end, and reorganize the whole string
We stop once we reach the marker: the current string is the marker followed by the organized uppercase letters. We replace the marker by a space and return it.

Related

Using recursion to concatenate ONLY the letters of an input string into a single output string

"Given a string of both letters and special characters/numbers, use recursion to concatenate the letters into a single string and return it."
My code is below, I'm still learning recursion and am stuck in trying to trace it. I tried a bunch of different lines in this code but idk how to fix what I do have so far:
def decoder(encryptedStr):
if len(encryptedStr) != 0:
if encryptedStr[0].isalpha() == True:
decoded = encryptedStr[0]
decoded.join(decoder(encryptedStr[1:]))
print(decoded)
else:
decoder(encryptedStr[1:])
I haven't had it return anything yet because I'm struggling with the part where I have to join the new letters to the output string. Instead of .join I also tried:
decoded += decoder(encryptedStr[1:])
but it doesn't work bc Nonetype??
Your main issue is that you didnt return, but there are some issues with your approach that make this more complex than need-be.
Think tail-first when doing recursion- What is your end condition, and how do you decide to continue. Typically with this kind of method you do something like, 1) process a single value in the list, 2) let the recursive method handle the rest of it, 3) combine the results.
An easy indicator of the tail-first return here would be to return nothing if the string is empty:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
...
Now in each run we want to operate on one letter and pass the rest to a recursive call. Ignoring the special character requirement, you'd get something like this:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
return first + rest
Now we can handle the special case where we want to omit letters.
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
if not first.isalpha():
first = ""
return first + rest
And that's all there is to it!
Bonus for some refactoring:
def clean(letter):
return letter if letter.isalpha() else ""
def decoder(encrypted):
if len(encrypted) == 0:
return ""
return clean(encrypted[0]) + decoder(encrypted[1:])
There's a bunch of problems here:
I don't think join does what you want it to do in that case. If you want to add some strings together simply use +=. join would insert decoded character between whatever decoder(encryptedStr[1:]) returns.
You don't have a case for len(encryptedStr) == 0, so it returns default value of None. That's why you cannot append it's results to decoded.
Return immediately if there is nothing to do. Otherwise take the first letter if it matches the condition and add the result of the recursive call (where the parameter is the current encrypted string without the first character).
def decoder(encrypted):
if not encrypted:
return ''
decrypted = encrypted[0] if encrypted[0].isalpha() else ''
return decrypted + decoder(encrypted[1:])
print(decoder('Abc123rtZ5'))
The result is AbcrtZ.
Bonus info (as #JonSG mentioned in the comments):
Run this with print(decoder('A' * 1000)) and you'll see why recursion is a bad idea for this task.
Every recursive function must have a base condition that stops the recursion or else the function calls itself infinitely.
To recursively concatenate ONLY the letters of an input string into a single output string:
some_string = "I2L4o2v3e+P;y|t!o#n"
def decoder(encryptedStr, decoded = ""):
if len(encryptedStr) == 0: # Base condition
return decoded
if encryptedStr[0].isalpha():
decoded += encryptedStr[0]
return decoder(encryptedStr[1:], decoded)
# If the char in the index [0] is not a letter it will be sliced out.
return decoder(encryptedStr[1:], decoded)
print(decoder(some_string))
Output:
ILovePython

python Question: Could any one explain how the above class reversed the string return val? sorry coming back to python after many years [duplicate]

I want to use recursion to reverse a string in python so it displays the characters backwards (i.e "Hello" will become "olleh"/"o l l e h".
I wrote one that does it iteratively:
def Reverse( s ):
result = ""
n = 0
start = 0
while ( s[n:] != "" ):
while ( s[n:] != "" and s[n] != ' ' ):
n = n + 1
result = s[ start: n ] + " " + result
start = n
return result
But how exactly do I do this recursively? I am confused on this part, especially because I don't work with python and recursion much.
Any help would be appreciated.
def rreverse(s):
if s == "":
return s
else:
return rreverse(s[1:]) + s[0]
(Very few people do heavy recursive processing in Python, the language wasn't designed for it.)
To solve a problem recursively, find a trivial case that is easy to solve, and figure out how to get to that trivial case by breaking the problem down into simpler and simpler versions of itself.
What is the first thing you do in reversing a string? Literally the first thing? You get the last character of the string, right?
So the reverse of a string is the last character, followed by the reverse of everything but the last character, which is where the recursion comes in. The last character of a string can be written as x[-1] while everything but the last character is x[:-1].
Now, how do you "bottom out"? That is, what is the trivial case you can solve without recursion? One answer is the one-character string, which is the same forward and reversed. So if you get a one-character string, you are done.
But the empty string is even more trivial, and someone might actually pass that in to your function, so we should probably use that instead. A one-character string can, after all, also be broken down into the last character and everything but the last character; it's just that everything but the last character is the empty string. So if we handle the empty string by just returning it, we're set.
Put it all together and you get:
def backward(text):
if text == "":
return text
else:
return text[-1] + backward(text[:-1])
Or in one line:
backward = lambda t: t[-1] + backward(t[:-1]) if t else t
As others have pointed out, this is not the way you would usually do this in Python. An iterative solution is going to be faster, and using slicing to do it is going to be faster still.
Additionally, Python imposes a limit on stack size, and there's no tail call optimization, so a recursive solution would be limited to reversing strings of only about a thousand characters. You can increase Python's stack size, but there would still be a fixed limit, while other solutions can always handle a string of any length.
I just want to add some explanations based on Fred Foo's answer.
Let's say we have a string called 'abc', and we want to return its reverse which should be 'cba'.
def reverse(s):
if s == "":
return s
else:
return reverse(s[1:]) + s[0]
s = "abc"
print (reverse(s))
How this code works is that:
when we call the function
reverse('abc') #s = abc
=reverse('bc') + 'a' #s[1:] = bc s[0] = a
=reverse('c') + 'b' + 'a' #s[1:] = c s[0] = a
=reverse('') + 'c' + 'b' + 'a'
='cba'
If this isn't just a homework question and you're actually trying to reverse a string for some greater goal, just do s[::-1].
def reverse_string(s):
if s: return s[-1] + reverse_string(s[0:-1])
else: return s
or
def reverse_string(s):
return s[-1] + reverse_string(s[0:-1]) if s else s
I know it's too late to answer original question and there are multiple better ways which are answered here already. My answer is for documentation purpose in case someone is trying to implement tail recursion for string reversal.
def tail_rev(in_string,rev_string):
if in_string=='':
return rev_string
else:
rev_string+=in_string[-1]
return tail_rev(in_string[:-1],rev_string)
in_string=input("Enter String: ")
rev_string=tail_rev(in_string,'')
print(f"Reverse of {in_string} is {rev_string}")
s = input("Enter your string: ")
def rev(s):
if len(s) == 1:
print(s[0])
exit()
else:
#print the last char in string
#end="" prints all chars in string on same line
print(s[-1], end="")
"""Next line replaces whole string with same
string, but with 1 char less"""
return rev(s.replace(s, s[:-1]))
rev(s)
if you do not want to return response than you can use this solution. This question is part of LeetCode.
class Solution:
i = 0
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
if self.i >= (len(s)//2):
return
s[self.i], s[len(s)-self.i-1] = s[len(s)-self.i-1], s[self.i]
self.i += 1
self.reverseString(s)

A "string index out if range" Python error

I´ve searched for other "string index out of range" cases, but they were not useful for me, so I wanted to search for help here.
The program has to do this: "Write a function kth_word(s, k) that given a string s and an integer k≥ 1 returns the kth word in string s. If s has less than k words it returns the empty string. We assume all characters of s are letters and spaces. Warning: do not use the split string method."
Here is my code:
def kth_word(s, k):
new =""
word_count = 0
for i in range(0, len(s)):
if s[i] == " " and s[i+1] != " ":
word_count+=1
#try to find how many characters to print until the space
if word_count == k-1:
while i!= " " and i<=len(s): #if it is changed to i<len(s), the output is strange and wrong
new+=s[i]
i=i+1
print(new) #check how new is doing, normally works good
return new
print(kth_word('Alea iacta est', 2))
(I tried my best to implement the code in a right way, but i do not know how)
And depending on the place where you live return new it gives or an error or just an empty answer
You iterate from 0 to len(s)-1 in your first for loop, but you're addressing i+1 which, on the last iteration, is len(s).
s[len(s)] is an IndexError -- it is out of bounds.
Additionally your while loop is off-by-one.
while i!= " " and i<=len(s):
# do something referencing s[i]
Your first condition makes no sense (i is a number, how could it be " "?) and your second introduces the same off-by-one error as above, where i is maximally len(s) and s[len(s)] is an error.
Your logic is a bit off here, too, since you're wrapping this inside the for loop which is already referencing i. This appears to be a takewhile loop, but isn't really doing that.
Warning: do not use the split string method.
So groupby / islice from itertools should work:
from itertools import groupby, islice
def kth_word(s, k):
g = (j for i, j in groupby(s, key=lambda x: x==' ') if not i)
return ''.join(next(islice(g, k-1, k), ''))
words = 'Alea iacta est'
res = kth_word(words, 2) # 'est'
We handle StopIteration errors by setting the optional parameter in next to ''.
You're not allowed to use str.split. If you could, the answer would just be:
def kth_word(s, k):
return s.split()[k]
But if you could write a function that does the same thing str.split does, you could call that instead. And that would certainly show that you understand everything the assignment was testing for—how to loop over strings, and do character-by-character operations, and so on.
You can write a version with only the features of Python usually taught in the first week:
def split(s):
words = []
current = ''
for ch in s:
if ch.isspace():
if current:
words.append(current)
current = ''
else:
current += ch
if current:
words.append(current)
return words
If you know additional Python features, you can improve it in a few ways:
Build current as a list instead of a str and ''.join it.
Change those append calls to yield so it splits the string lazily (even better than str.split).
Use str.find or str.index or re.search to find the next space instead of searching character by character.
Abstract out the space-finding part into a general-purpose generator—or, once you realize what you want, find that function in itertools.
Add all of the features we're missing from str.split, like the ability to pass a custom delimiter instead of breaking on any whitespace.
But I think even the basic version—assuming you understand it and can explain how it works—ought to be enough to get an A on the assignment.
And, more importantly, you're practicing the best way to solve problems: reduce them to simpler problems. split is actually easier to write than kth_word, but once you write split, kth_word becomes trivial.
You actually have at least five problems here, and you need to fix all of them.
First, as pointed out by Adam Smith, this is wrong:
for i in range(0, len(s)):
if s[i] == " " and s[i+1] != " ":
This loops with i over all the values up to but not including len(s), which is good, but then, if s[i] is a space, it tries to access s[i+1]. So, if your string ended with a space, you would get an IndexError here.
Second, as ggorlen pointed out in a comment, this is wrong:
while i!= " " and i<=len(s):
new+=s[i[]
When i == len(s), you're going to try to access s[i], which will be an IndexError. In fact, this is the IndexError you're seeing in your example.
You seem to realize that's a problem, but refuse to fix it, based on this comment:
#if it is changed to i<len(s), the output is strange and wrong
Yes, the output is strange and wrong, but that's because fixing this bug means that, instead of an IndexError, you hit the other bugs in your code. It's not causing those bugs.
Next, you need to return new right after doing the inner loop, rather than after the outer loop. Otherwise, you add all of the remaining words rather than just the first one, and you add them over and over, once per character, instead of just adding them once.
You may have been expecting that doing that i=i+1 would affect the loop variable and skip over the rest of the word, but (a) it won't; the next time through the for it just reassigns i to the next value, and (b) that wouldn't help anyway, because you're only advancing i to the next space, not to the end of the string.
Also, you're counting words at the space, but then you're iterating from that space until the next one. Which means (except for the first word) you're going to include that space as part of the word. So, you need to do an i += 1 before the while loop.
Although it would probably be a lot more readable to not try to reuse the same variable i, and also to use for instead of while.
Also, your inner loop should be checking s[i] != " ", not i!=" ". Obviously the index, being a number, will never equal a space character.
Without the previous fix, this would mean you output iacta est
with an extra space before it—but with the previous fix, it means you output nothing instead of iacta.
Once you fix all of these problems, your code works:
def kth_word(s, k):
word_count = 0
for i in range(0, len(s) - 1):
if s[i] == " " and s[i+1] != " ":
word_count+=1
#try to find how many characters to print until the space
if word_count == k-1:
new =""
j = i+1
while j < len(s) and s[j] != " ":
new+=s[j]
j = j+1
print(new) #check how new is doing, normally works good
return new
Well, you still have a problem with the first word, but I'll leave it to you to find and fix that one.
Your use of the variable 'i' in both the for loop and the while loop was causing problems. using a new variable, 'n', for the while loop and changing the condition to n < len(s) fixes the problem. Also, some other parts of your code required changing because either they were pointless or not compatible with more than 2 words. Here is the fully changed code. It is explained further down:
def kth_word(s, k):
new = ""
word_count = 0
n = 0
for i in range(0, len(s) - 1):
if s[i] == " " and s[i + 1] != " ":
word_count += 1
#try to find how many characters to print until the space
if word_count < k:
while n < len(s): #if it is changed to i<len(s), the output is strange and wrong
new+=s[n]
n += 1
print(new) #check how new is doing, normally works good
return new
print(kth_word('Alea iacta est', 2))
Explanation:
As said in Adam Smith's answer, 'i' is a number and will never be equal to ' '. That part of the code was removed because it is always true.
I have changed i = i + 1 to i += 1. It won't make much difference here, but this will help you later when you use longer variable names. It can also be used to append text to strings.
I have also declared 'n' for later use and changed for i in range(0, len(s)): to for i in range(0, len(s) - 1): so that the for loop can't go out of range either.
if word_count == k-1: was changed to if word_count < k: for compatibility for more words, because the former code only went to the while loop when it was up to the second-last word.
And finally, spaces were added for better readability (This will also help you later).

How change the cycle for getting correct answers?

I have a task to create a function that reverse any string character inside the regular bracket sequence, starting from the innermost pair. The string sequence can have, spaces, punctuation marks, letters and brakets. So the result should be sting.
Example
For string
s = "a(bc)de"
the output should be
reverseParentheses(s) = "acbde".
I have wrote the following code to solve this problem:
s_i = s
for i in range(s.count('(')):
# reverse letters inside parenthesis
s_i = s_i.replace(s_i[s_i.rindex('(')+1:s_i.index(')')], s_i[s_i.rindex('(')+1:s_i.index(')')][::-1])
# delete outward parenthesis
s_i =s_i[:s_i.rindex('(')] + s_i[s_i.rindex('(')+1:]
# delete inward parenthesis
s_i =s_i[:s_i.index(')')] + s_i[s_i.index(')')+1:]
i += 1
print(s_i)
However, I become false results for following strings:
s = "abc(cba)ab(bac)c"
It should be
abcabcabcabc
I get
abccabbaabcc
And
s = "The ((quick (brown) (fox) jumps over the lazy) dog)"
It should be like this:
The god quick nworb xof jumps over the lazy
But I get only:
The god quick xof nworb jumps over the lazy
How should I correct or adjust my code for becoming right results for last two examples?
Code Adjustment
I have tried to take into consideration answers and hints but I could not use recursion. I adressed the problem with the parantacies when there are just two of them located as: "..(...) (...).., .."
So I made the following code:
def reverse(s):
#ensure parens are in pairs
if '(' not in s and ')' not in s:
while '(' in s:
s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
s = s[:s.index(')')] + s[s.index(')')+1:]
return s
else:
if (s[s.index(')'):s.rindex('(')+1] == ''):
while '(' in s:
s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
s = s[:s.index(')')] + s[s.index(')')+1:]
return s
elif (s[s.index(')'):s.rindex('(')+1] != ''):
betw = s[s.index(')')+1:s.rindex('(')]
part1 = s[:s.index(')')+1]
part2 = s[s.rindex('('):]
part1 = part1.replace(part1[part1.rindex('(')+1:part1.index(')')], part1[part1.rindex('(')+1:part1.index(')')][::-1])
part1 = part1[:part1.rindex(')')]
part2 = part2.replace(part2[part2.rindex('(')+1:part2.index(')')], part2[part2.rindex('(')+1:part2.index(')')][::-1])
part2 = part2[part2.rindex('(')+1:]
s = part1+betw+part2
s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
s = s[:s.index(')')] + s[s.index(')')+1:]
while '(' in s:
s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
s = s[:s.index(')')] + s[s.index(')')+1:]
return s
else:
while '(' in s:
s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
s = s[:s.index(')')] + s[s.index(')')+1:]
return s
However, I think that it can not perform well for the following example:
s = "abc(147)ab(123)c(12)asd"
The answer should be: "abc741ab321c21asd" but I get "abc12c321ba147asd"
What should be changed in order to get the correct answer?
rather than doing this manually, use the regular expression module re that specialize in string manipulation
import re
def reverseParentheses(s):
def reverse_interior(m):
s = m.group()
return s[-2:0:-1]
old = ""
while old != s:
old = s
s = re.sub(r'(\([^\(\)]*\))',reverse_interior,s)
return s
assert reverseParentheses("a(bc)de") == "acbde"
assert reverseParentheses("abc(cba)ab(bac)c") == "abcabcabcabc"
assert reverseParentheses("The ((quick (brown) (fox) jumps over the lazy) dog)") == "The god quick nworb xof jumps over the lazy"
assert reverseParentheses("((ob))") == "ob"
here the expression '(\([^\(\)]*\))' would search anything that is between ( and ) that is a one of characters defined in [^\(\)]* which in turn means any numbers character that is not a ( or ), this way it will search the innermost group that match, then I use the function re.sub to replace those in the string with that auxiliary function, that take a string of the form "(xyz)" and return "zyx". As this only work for the innermost group, the process should be repeated while there are changes to be made, hence the loop.
The reason your solution isn't working is because it is mismatching parentheses:
"abc(cba)ab(bac)c"
" (cba)ab(bac) "
" )ab( "
Your method ultimately won't work: Instead, I recommend you figure out a better way of figuring out which parens match:
def find_paren_set(str):
# magic
return left, right
Once you have that capability, you can then just while has_parens(str): through until you're done.
Additional note: Each time you reverse a section, the inner parens will get swapped, ((ob)) will become )bo(.
Since it appears that any number of () pairs could occur, I would recommend implementing a recursive function that can be called as long as pairs of parens still exist at a level outside the first:
def reverseParentheses(s):
# ensure parens are in pairs
assert '(' in s and ')' in s
while '(' in s:
# Go through and swap strings/letters in the innermost parens only.
# Then reassign `s` to that newly formatted string
# (after taking out those parens)
# Call the function again until it purges the string of all parens
reverseParentheses(s)
return s

Python reversing a string using recursion

I want to use recursion to reverse a string in python so it displays the characters backwards (i.e "Hello" will become "olleh"/"o l l e h".
I wrote one that does it iteratively:
def Reverse( s ):
result = ""
n = 0
start = 0
while ( s[n:] != "" ):
while ( s[n:] != "" and s[n] != ' ' ):
n = n + 1
result = s[ start: n ] + " " + result
start = n
return result
But how exactly do I do this recursively? I am confused on this part, especially because I don't work with python and recursion much.
Any help would be appreciated.
def rreverse(s):
if s == "":
return s
else:
return rreverse(s[1:]) + s[0]
(Very few people do heavy recursive processing in Python, the language wasn't designed for it.)
To solve a problem recursively, find a trivial case that is easy to solve, and figure out how to get to that trivial case by breaking the problem down into simpler and simpler versions of itself.
What is the first thing you do in reversing a string? Literally the first thing? You get the last character of the string, right?
So the reverse of a string is the last character, followed by the reverse of everything but the last character, which is where the recursion comes in. The last character of a string can be written as x[-1] while everything but the last character is x[:-1].
Now, how do you "bottom out"? That is, what is the trivial case you can solve without recursion? One answer is the one-character string, which is the same forward and reversed. So if you get a one-character string, you are done.
But the empty string is even more trivial, and someone might actually pass that in to your function, so we should probably use that instead. A one-character string can, after all, also be broken down into the last character and everything but the last character; it's just that everything but the last character is the empty string. So if we handle the empty string by just returning it, we're set.
Put it all together and you get:
def backward(text):
if text == "":
return text
else:
return text[-1] + backward(text[:-1])
Or in one line:
backward = lambda t: t[-1] + backward(t[:-1]) if t else t
As others have pointed out, this is not the way you would usually do this in Python. An iterative solution is going to be faster, and using slicing to do it is going to be faster still.
Additionally, Python imposes a limit on stack size, and there's no tail call optimization, so a recursive solution would be limited to reversing strings of only about a thousand characters. You can increase Python's stack size, but there would still be a fixed limit, while other solutions can always handle a string of any length.
I just want to add some explanations based on Fred Foo's answer.
Let's say we have a string called 'abc', and we want to return its reverse which should be 'cba'.
def reverse(s):
if s == "":
return s
else:
return reverse(s[1:]) + s[0]
s = "abc"
print (reverse(s))
How this code works is that:
when we call the function
reverse('abc') #s = abc
=reverse('bc') + 'a' #s[1:] = bc s[0] = a
=reverse('c') + 'b' + 'a' #s[1:] = c s[0] = a
=reverse('') + 'c' + 'b' + 'a'
='cba'
If this isn't just a homework question and you're actually trying to reverse a string for some greater goal, just do s[::-1].
def reverse_string(s):
if s: return s[-1] + reverse_string(s[0:-1])
else: return s
or
def reverse_string(s):
return s[-1] + reverse_string(s[0:-1]) if s else s
I know it's too late to answer original question and there are multiple better ways which are answered here already. My answer is for documentation purpose in case someone is trying to implement tail recursion for string reversal.
def tail_rev(in_string,rev_string):
if in_string=='':
return rev_string
else:
rev_string+=in_string[-1]
return tail_rev(in_string[:-1],rev_string)
in_string=input("Enter String: ")
rev_string=tail_rev(in_string,'')
print(f"Reverse of {in_string} is {rev_string}")
s = input("Enter your string: ")
def rev(s):
if len(s) == 1:
print(s[0])
exit()
else:
#print the last char in string
#end="" prints all chars in string on same line
print(s[-1], end="")
"""Next line replaces whole string with same
string, but with 1 char less"""
return rev(s.replace(s, s[:-1]))
rev(s)
if you do not want to return response than you can use this solution. This question is part of LeetCode.
class Solution:
i = 0
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
if self.i >= (len(s)//2):
return
s[self.i], s[len(s)-self.i-1] = s[len(s)-self.i-1], s[self.i]
self.i += 1
self.reverseString(s)

Categories

Resources