My goal is to concatenate two strings using recursion.
The input would be str1 = ("long string") str2 = ("yes so long")
and the output would be: 'lyoensg ssto rlionngg'
Assumptions are that both strings are the same length.
My code as of now is:
def concat_order(str1, str2):
if len(str1) == 0:
return 'complete'
i = 0
new_str = str1[i] + str2[i]
i = i + 1
return concat_order(str1[1:], str2[1:])
return new_str
print(concat_order("long string"', "yes so long"))
Im sure Im no where close but I am struggling with making it loop.
I am not allowed to use loops but I can use if statements
You don't need to pass 'i' like this: new_str = str1[i] + str2[i]
as you are already returning string excluding previous character: return concat_order(str1[1:], str2[1:]).
Also a function can not have two return statements like this:
return concat_order(str1[1:], str2[1:])
return new_str
any statement after return statement won't execute. That's why we write multiple return statements in some conditional statement like if else
Modified your program to give the correct answer.
def concat_order(str1, str2, new_str):
if len(str1) == 0:
return new_str
new_str += str1[0] + str2[0]
return concat_order(str1[1:], str2[1:], new_str)
ans = ""
print(concat_order("long string", "yes so long", ans))
or you can do this:
def concat_order(str1, str2):
if len(str1) == 0:
return ''
return str1[0] + str2[0] + concat_order(str1[1:], str2[1:])
print(concat_order("long string", "yes so long"))
as the control flow reaches base case, we already have what we wanted so don't return anything.
Recursion just needs a base case and a recursion case. Here your base case is a little strange. You never want the string "complete" so you shouldn't return that. The base case when str1 is empty should just return the empty string.
Then you just need to take the first letter from each string, concat, and concat with recursion of the rest:
def concat_order(str1, str2):
if len(str1) == 0:
return ''
a, b = str1[0], str2[0]
return a + b + concat_order(str1[1:], str2[1:])
concat_order("long string", "yes so long")
# 'lyoensg ssot rlionngg'
Note the extra space is the correct behavior unless there's a requirement to prevent them.
def concat_strings_rec(str1, str2):
if len(str1) < 1:
return ""
else:
curStr = str1[0] + str2[0]
return curStr + concat_strings_rec(str1[1:], str2[1:])
def concat_string_iter(str1, str2):
return "".join([str1[i] + str2[i] for i in range(len(str1))])
string1 = "long string"
string2 = "yes so long"
print(concat_strings_rec(string1, string2))
print(concat_string_iter(string1, string2))
Expected output:
lyoensg ssot rlionngg
lyoensg ssot rlionngg
Recursive solution
You need the base case which is when the array length is 0. In that case return an empty string. In all other cases return the first element of each string concatenated with the return value of the recursive call to concat_strings_rec(). Remember to decrease the array size for the recursive calls!
Iterative solution
The iterative solution just loops through the array concatenating each character within the two strings and putting the concatenated first, second, third,... character in an array. Then use "".join() to concatenate those two character strings in the array to a complete string.
Recommendation
Don't use the recursive solution as it just consumes more memory due to the call stack and it will be slower and since Python has a limit on the number of calls on the call stack it will fail for larger strings.
Related
I am trying to create a function in Python which allows me to know if a string contains a letter "y" which appears in the beginning of a word and before a consonant. For example, the sentence "The word yes is correct but the word yntelligent is incorrect" contains the "y" of the word "yncorrect", so the function has to return True. In addition, it has to return true if the "y" is in capital letters and verifies those same conditions.
I have done it in the following way and it appears as if the program works but I was asked to use the method for strings in Python find and I havent't been able to include it. Any hint about how to do it using the method find? Thank you very much.
def function(string):
resultado=False
consonants1="bcdfghjklmnñpqrstvwxyz"
consonants2="BCDFGHJKLMNÑPQRSTVWXYZ"
for i in range(0,len(string)):
if string[i]=="y" and string[i-1]==" " and string[i+1] in consonants1:
resultado=True
break
if string[i]=="Y" and string[i-1]==" " and string[i+1] in consonants2:
resultado=True
break
return resultado
print(function("The word yes is correct but the word yntelligent is incorrect"))
Basically it is better to use re
consonants1="BCDFGHJKLMNÑPQRSTVWXYZ"
for i in consonants1:
if (a:= string.upper().find(f' Y{i}')) != -1:
print(...)
break
I think the function you want isn't find, but finditer from the package 're' (find will only give you the first instance of y, while finditer will return all instances of y)
import re
import string
consonants = string.ascii_lowercase
vowels = ['a', 'e', 'i', 'o', 'u']
for vowel in vowels:
consonants.remove(vowel)
def func(string):
for x in re.finditer('y', string.lower()):
if string[x.start() + 1] in consonants:
return True
return False
The function find returns the index at which the string first begins or is found. So, it returns the first index, else -1. This won't work for your use cases, unless you make it a bit more complicated.
Method One: Check every combination with find.
You have to two results, one to check if its the first word, or if its in any other word. Then return True if they hit. Otherwise return false
def function(string):
consonants1="bcdfghjklmnñpqrstvwxyz"
string = string.lower()
for c in consonants1:
result1 = string.find(" y" + c)
result2 = string.find("y" + c)
if result1 != 1 or result2 == 0:
return True
return False
Method Two: loop through find results.
You can use .find but it will be counter-intuitive. You can use .find and loop through each new substring excluding the past "y/Y", and do a check each time you find one. I would also convert the string to .lower() (convert to lowercase) so that you don't have to worry about case sensitivity.
def function(string):
consonants1="bcdfghjklmnñpqrstvwxyz"
string = string.lower()
start_index = 0
while start_index < len(string):
temp_string = string[start_index+1:end] ## add the 1 so that you don't include the past y
found_index = temp_string.find("y")
if found_index == -1: return False
og_index = start_index + found_index
## check to see if theres a " yConsonants1" combo or its the first word without space
if (string[og_index - 1] == " " and string[og_index+1] in consonants1) or (string[og_index+1] in consonants1 and og_index == 0):
return True
else:
start_index = og_index
return False
Here's how I would go about solving it:
Look up what the find function does. I found this resource online which says that find will return the index of the first occurrence of value (what's passed into the function. If one doesn't exist, it returns -1.
Since we're looking for combinations of y and any consonant, I'd just change the arrays of your consonants to be a list of all the combinations that I'm looking for:
# Note that each one of the strings has a space in the beginning so that
# it always appears in the start of the word
incorrect_strings = [" yb", " yc", ...]
But this won't quite work because it doesn't take into account all the permutations of lowercase and uppercase letters. However, there is a handy trick for handling lowercase vs. uppercase (making the entire string lowercase).
string = string.lower()
Now we just have to see if any of the incorrect strings appear in the string:
string = string.lower()
incorrect_strings = [" yb", " yc", ...]
for incorrect_string in incorrect_strings:
if string.find(incorrect_string) >= 0:
# We can early return here since it contains at least one incorrect string
return True
return False
To be honest, since you're only returning a True/False value, I'm not too sure why you need to use the find function. Doing if incorrect_string in string: would work better in this case.
EDIT
#Barmar mentioned that this wouldn't correctly check for the first word in the string. One way to get around this is to remove the " " from all the incorrect_strings. And then have the if case check for both incorrrect_string and f" {incorrect_string}"
string = string.lower()
incorrect_strings = ["yb", "yc", ...]
for incorrect_string in incorrect_strings:
if string.find(incorrect_string) >= 0 or string.find(f" {incorrect_string}"):
# We can early return here since it contains at least one incorrect string
return True
return False
I want to write a function using recursion that copies the first character from the first word in the list, 2 characters from second word and so on.
Here is what I wrote:
def word(words_list, i):
if len(words_list[i]) <= 1:
return words_list[i]
else:
return words_list[i][0] + get_code(words_list[i][1:],i)
i += 1
>>>print(word(["tigers", "zebras", "and", "lions"], 0))
tzeandlion # Expected
ti # received
How can I go about this? Any help pls?
With your setting, an option might be:
def word(words_list, i=0):
if i >= len(words_list):
return ''
return words_list[i][:i+1] + word(words_list, i + 1)
print(word(["tigers", "zebras", "and", "lions"])) # tzeandlion
With a different parameterization:
def word(words_list, length=1):
if not words_list:
return ''
return words_list[0][:length] + word(words_list[1:], length + 1)
If you were not required to use recursion, the following might be simpler and perhaps faster:
output = ''.join(word[:length] for length, word in enumerate(words_list, start=1))
: Write a function, named foldStrings(string1, string2) that takes, as arguments, two strings. If
the two strings are equal in length, the function returns a string that is formed by alternating characters in each of the two strings. If the two strings are not equal in length, the function returns the string “The two strings are not
equal in length.”
For example, >>>foldStrings(“abc”, “def”)
should return the string “adbecf” and >>>foldStrings(“a”, “bc”)
should return the string “The two strings are not equal in length.”
This is what I have so far:
def foldStrings(str1, str2):
newStr = ""
counter = 0
if len(str2) == len(str1):
while counter < len(str2):
for element in str1:
for index in str2:
newStr = newStr + element
newStr = newStr + index
counter += 1
return newStr
else:
return "The two Strings are not equal in length"
and it prints this: 's1s2s3s4s5s6a1a2a3a4a5a6n1n2n3n4n5n6t1t2t3t4t5t6o1o2o3o4o5o6s1s2s3s4s5s6'
instead of this:
's1a2n3t4o5s6'
You have unnecessarily complicated the problem with three nested loops, when a single loop is required.
Replace:
while counter < len(str2):
for element in str1:
for index in str2:
newStr = newStr + element
newStr = newStr + index
counter += 1
return newStr
With:
for index in range(len(str1)) :
newStr = newStr + str1[index] + str2[index]
return newStr
In the original code, if the string length was for example 6, your code says:
Repeat 6 times:
for every character in str1
for every character in str1
do stuff
so that do stuff is executed 6 x 6 x 6 times! You only want to execute it 6 times suggesting a single loop.
What you were doing wrong was not a python specific issue, but rather an issue with your algorithmic and logical thinking. Mathematically the problem suggests a single iteration, while you had three - nested. In this case you might have manually walked through the code or used a debugger to step through it to demonstrate flaw in thinking here.
You can skip using a counter variable by using range
def foldStrings(str1, str2):
if len(str1) != len(str2):
return "The two Strings are not equal in length"
# This should really be a raise ValueError("The two Strings are not equal in length")
newStr = ''
for i in range(len(str1)):
newStr += str1[i] + str2[i]
return newStr
Here's a slightly more compact way to replace the for loop
newStr = ''.join([x for pair in zip(str1, str2) for x in pair])
#Simple way to do it
def foldStrings(str1, str2):
newStr = ""
counter = 0
if len(str2) == len(str1):
while counter < len(str2):
newStr = newStr + str1[counter] + str2[counter]
counter += 1
return newStr
else:
return "The two Strings are not equal in length"
This is what I have so far:
def count2(char,text):
if len(text)==0:
return 0
else:
if char==count2(char,text[:-1]):
return (1+count2(char,text[:-1]))
else:
return False
It will just go to false, but I am trying to count how many times "char" equals each character of "text."
Your base case looks correct. For your recursive case, lets take a look at the logic. There are two possible cases:
If the first character of the current string is the one you are looking for. In this case, you should return 1 + the count of the character in the rest of the string.
If the first character is not equal, then you should just return the count of the character in the rest of the string.
The function thus becomes
def count2(char,text):
if len(text)== 0:
return 0
count = 1 if text[0] == char else 0
return count + count2(char, text[1:])
Python has a neat way of treating True/False as 1/0, so you could simply write something like this:
def numberofcharacters(char, text):
if len(text) == 0:
return 0
return (text[-1] == char) + numberofcharacters(char, text[:-1])
def count2(char, text):
charCount = 0
for i in text:
if i == char:
charCount += 1
return(charCount)
Here's my two sense on the matter, a less complicated solution.
Best way to achieve this will be using string.count() function as:
>>> 'engineering'.count('e')
3
But I believe it is the part of some assignment. Since you are specific for using recursive function, below is the sample code to achieve this as:
def numberofcharacters(my_char, my_string):
if my_string:
if my_char == my_string[0]:
return 1 + numberofcharacters(my_char, my_string[1:])
else:
return numberofcharacters(my_char, my_string[1:])
else:
return 0
This solution can be further simplified as:
def numberofcharacters(my_char, my_string):
return ((my_char == my_string[0]) + numberofcharacters(my_char, my_string[1:])) if my_string else 0
Sample run:
>>> numberofcharacters('e','engineering')
3
Using a list comprehension makes things clear :
def numberofcharacters(my_char, my_string):
return len([c for c in my_string if c == my_char])
I'm doing some python online tutorials, and I got stuck at an exercise:A palindrome is a word which is spelled the same forwards as backwards. For example, the word
racecar
is a palindrome: the first and last letters are the same (r), the second and second-last letters are the same (a), etc. Write a function isPalindrome(S) which takes a string S as input, and returns True if the string is a palindrome, and False otherwise.
These is the code I wrote :
def isPalindrome(S):
if S[0] == S[-1]
return print("True")
elif S[0] == S[-1] and S[1] == S[-2] :
return print("True")
else:
return print("False")
But, if the word is for example ,,sarcas,, , the output is incorect. So I need a fix to my code so it works for any word.
A one line solution but O(n) and memory expensive is :
def isPalindrome(word) : return word == word[::-1]
A O(n/2) solution that uses the same amount of memory is:
def palindrome(word):
for i in range(len(word)//2):
if word[i] != word[-1-i]:
return False
return True
This is the trick #LennartRegebro mentioned
def is_palindrome(text):
text = text.replace(' ', '')
text = text.casefold()
if list(text) == list(reversed(text)):
return True
else:
return False
Try this
word='malayalam'
print(word==word[::-1])
The best way to check a word is palindrome or not in Python is as below:
var[::] == var[::-1]
But, it is very important to understand that Python creates a new copy of string when you do var[::-1]
Python internally don't know if the reverse will result in same string or not. So, it's coded in a way where it creates a new copy of it.
So, when you try var[::1] is var[::-1] you will get FALSE.
Here is my solution.
S = input("Input a word: ")
def isPalindrome(S):
for i in range(0, len(S)):
if S[0 + i] == S[len(S) - 1]:
return "True"
else:
return "False"
print(isPalindrome(S))
Here's my solution:
def isPalindrome(S):
l = len(S)-1
for i in range(0,l):
if S[i]!=S[l-i]:
return False
return True
Another way of doing it using recursion is:
def isPalindrome(word):
if len(word) <= 1: return True
return (word[0] == word[-1]) and isPalindrome(word[1:-1])
this is my solution
S = input("Input a word: ")
def isPalindrome(S):
for i in range(0, len(S)):
if S[0 + i] == S[len(S) - 1]:
return "True"
else:
return "False"
print(isPalindrome(S))
This is beaten to death, but I have some ideas if Python is implemented in C. If ...
The code word[::-1] iterates to string end to make a copy which discounts the word == word[::-1] code speed and memory efficiency factors
Iteration to the word middle is better then comparing a full word as characters were already tested - for odd words the middle letter is not to be tested
Additions and subtractions need to be used sparingly
Recursion is cool (and fun), but uses a lot of stack (unless optimized with tail recursion ... which likely becomes a for loop in the underlying code but less likely able to be flexible enough to impose code optimizations).
Bit shifts are traditionally faster than divides
The following is the code:
def isPalindrome(S):
last = len(S)
middle = last >> 1
for i in range(middle):
last -= 1
if(S[i] != S[last]):
return False
return(True)
print("\n".join([str((word, isPalindrome(word))) for word in ["abcdcba", "abcdBba", "abccba", "abcBba", "a", ""]]))
which yields:
('abcdcba', True)
('abcdBba', False)
('abccba', True)
('abcBba', False)
('a', True)
('', True)