Recursion and list comprehension? - python

is "lst[1:-1]" any different from "lst[1:]" ? in this situation:
def is_pal(string):
if len(string) < 2:
return True
elif string[0] != string[-1]:
return False
else:
return is_pal(string[1:-1])
The code is Checking whether string is palindrome.

-1 is exclusive, meaning [1:-1] will return the whole string except for the first (0) and the last characters.
BTW you can just reverse the text and check it that way:
def is_pal(string):
return string[::-1] == string

consider a string,
s = "abcdef"
s[1:] = "bcdef"
s[1:-1] = "bcde"
To check whether the given string is palindrome or not,
just compare the given string s with string s[::-1].

Related

identifying the substring when the number of characters in between don't matter

def checkPattern(x, string):
e = len(string)
if len(x) < e:
return False
for i in range(e - 1):
x = string[i]
y = string[i + 1]
last = x.rindex(x)
first = x.index(y)
if last == -1 or first == -1 or last > first:
return False
return True
if __name__ == "__main__":
x = str(input())
string = "hello"
if checkPattern(x, string) is True:
print('YES')
if checkPattern(x, string) is False:
print('NO')
So basically the code is supposed to identify a substring when the number of characters between the substring's letters don't matter. string = "hello" is supposed to be the substring. While the characters in between don't matter the order still matters so If I type "h.e.l.l.o" for example it's a YES, but if it's something like "hlelo" it's a NO. I sorta copied the base of the code and I'm still a little new to python so sorry if the question and code aren't clear.
Assuming I understand, and the input hlelo is No and the input h.e..l.l.!o is Yes, then the following code should work:
def checkPattern(x, string):
assert x and string, "Error. Both inputs should be non-empty. "
count_idx = 0 # index which counts where you are.
for letter in x:
if letter == string[count_idx]:
count_idx += 1 # increment to check the next string
if count_idx == len(string):
return True
# pattern was found if counter found matches equal to string length
return False
if __name__ == "__main__":
inp = input()
string = "hello"
if checkPattern(inp, string) is True:
print('YES')
if checkPattern(inp, string) is False:
print('NO')
Explaination: Regardless of the input string, x, you want to loop through each character of the search-string hello, to check if you find each character in the correct order. What my solution does is that it counts how many of the characters h, e, l, l, o it has found, starting from 0. If it finds a match for h, it moves on to check for a match for e, and so on. Ultimately, if you search through the entire string x, and the counter does not equal to the length of the search string (i.e. you could not find all the hello characters), it returns false.
EDIT: Small debug in the way the return worked. Instead returns if ever the counter goes over the length. Also added more examples given in comments
Here is my solution to this problem:
pattern = "hello"
def patternCheck(word, pattern) -> bool:
plist = list(pattern)
wlist = list(word)
for p in plist:
if p in wlist:
for _ in range(wlist.index(p) , -1, -1):
wlist.pop(_)
else:
return False
return True
print(patternCheck("h.e.l.l.o", pattern))
print(patternCheck("aalohel", pattern))
print(patternCheck("hhhhheeelllooo", pattern))
Explanation
First we convert our strings to a list
plist = list(pattern)
wlist = list(word)
Now we check using a for loop if every element in our pattern list is in the word list.
for p in plist:
if p in wlist:
If yes then we remove all the elements from index 0 to the index of that element.
for _ in range(wlist.index(p) , -1, -1):
wlist.pop(_)
We are removing elements in decreasing order of there indices to protect ourself from the IndexError: pop index out of range.
If the for loop ends normally then there was a match and we return True. Else if the element was not found in the word list in the first place then we return false as there is no match.

String Index out of range issue while making a function that counts vowels

I am trying to make a function that counts all the vowels using recursion and I keep getting string index out of range.
def vowels(s):
vowel = "AEIOUaeiou"
if s == " ":
return 0
if s[0] in vowel:
return 1 + vowels(s[1:])
else:
return vowels(s[1:])
print(vowels("Hello Planet Me"))
I am expecting a number of vowels in a string and I just get error on lines 8 and 10 repeating.
Change this line
if s == " ":
to
if s == "":
or
if len(s) == 0:
or, the best way is to use the boolean value of str type in python which returns a False if the str is empty.
if not s:
For the base case, an empty string is passed to the function and not a space character.
if you just change your initial if condition to
if not s:
return 0
you will be fine. that way you just return 0 if the string is empty an you do not get to the line containing s[1:] (which will raise the error you encountered).
a simple non-recursive way would be:
VOWELS = set("AEIOUaeiou")
def vowels(s):
return sum(char in VOWELS for char in s)
(as True is just an alias for 1 and False is an alias for 0 you can just sum the booleans).

Multiple characters in a string return a false boolean when trying to find a character in the string

#Function takes a character and a string and returns a boolean reflecting if
#the character is found in the string.
def isItThereS(letter, word):
letInWord = 0
for l in word:
if l == letter:
letInWord += 1
return letInWord == True
When I put it in the operator like
isItThereS("h","hello world")
True
but when I go to find a character that repeats like "l" or "o" it returns false.
isItThereS("l","hello world")
False
How would I go about getting that to not return false, but instead return True since the character is technically in the string?
you can simply use the in operator
def isItThereS(letter, word):
return letter in word
If you really want to use a custom function for that, change your return to return letInWord >= 1. As everything but 1 == True will evaluate to False. (So a more appropriate name for the function as is would be is_it_there_only_once).
Otherwise please use the solution provided by armak.

Python, checking if string consists only of 1's and 0's

I'm trying to write a function that checks if given string is binary or not. I'm using a while loop to browse characters in string one by one. If all of them are 0's or 1's it's going to return True, if anything is not - break the loop and return False.
I've just written this tiny part of code, but it doesn't run. Why?
def fnIsBin(string):
count = 0
while count < len(string):
character = string[count]
if character == '0' or character == '1':
print (count, character[count], "OK")
count = count+1
continue
else:
print (count, character[count], "ERROR")
return False
break
EDIT:
I also tried using 'set' to elude iterating with loop, but i don't quite get how does "set(string)" work. I got error that i cant consider it as a list. So how can I compare elements to 0 & 1?
def fnIsBin(string):
charactersList = set(string)
if len(charactersList) > 2:
return False
else:
if (charactersList[0] == '0' or charactersList[0] == '1') and (charactersList[1] == '0' or charactersList[1] == '1'): #there i made error, how to deal with it?
return True
else:
return False
Your function fails because you never actually return True. That means if the string is actually binary, you'd return None which Python would consider as False in any boolean check.
Simply add return True at the end of the function.
As #Barmar mentioned in the comment, you also print the value of character[count] instead of string[count] which would cause IndexError.
An easier way to check if the string is binary would be:
test_string = '010101'
all_binary = all(c in '01' for c in test_string)
There are many ways to do this:
Set:
fnInBin(s):
return set(s).issubset({'0', '1'}) and bool(s)
Regular expression:
fnInBin(s):
return bool(re.fullmatch('[01]+', s))
Integer conversion:
fnIsBin(s):
try:
int(s, 2)
return True
except ValueError:
return False
The last one will strip whitespace from the input, though.
fnIsBin('\n 1 ') == True
You can use the int object and give it a base. It will fail if the object passed doesn't consist of a binary representation. Recall that a binary string is base 2.
def fnIsBin(string):
try:
binary_repr = int(string, 2)
except ValueError as err
print("error: {0}".format(err))
return False
return True
You're printing OK for each character that's 0 or 1, even though there may be a later character that isn't. You need to wait until the end of the loop to know that it's really OK.
def fnIsBin(string):
for character in string:
if character != '0' and character != '1':
print (character, "ERROR")
return False
print "OK"
return True
There's no need for break after return False -- returning from the function automatically breaks out of the loop.
Here's one of the ways you could rewrite your function and maintain a similar structure (looping through each character):
def fnIsBin(string):
for character in string:
if not character in '01':
return False
return True
You could also do it using regular expressions:
import re
def fnIsBin(string):
return re.match(r'^[10]+$', '01') is not None
My two cents (A short way):
test_string = '010101'
result = set(test_string) <= {'0','1'}
print(result)
Using set(test_string) converts the string into list of characters,
{0,1,0,1,0,1}
Using <= operator checks whether the set on the left-side is a proper subset of the set on the right-hand side
Ultimately checking whether there are only 0 and 1 are in the string or not in an in-direct and mathematical way.
Adding a XOR solution to the mix:
bin_string = '010101'
for j in range(0, len(bin_string)):
if int(bin_string[j]) != 0 ^ int(bin_string[j]) != 1:
break

Python: Recursive Function to check if string is a palindrome

So I'm trying to create a recursive function to check if a word is a palindrome. It should ignore all non-alphabetical characters. Here is what I have so far.
def is_palindrome(text):
'''
A Recursive Function that returns True if the parameter, text, is a palindrome, False if not.
Ignores capitalization, punctuation, and spaces.
text: a String
returns True or False
'''
text = list(text)
if len(text) == 0:
return True
else:
if text[0].isalpha():
if text[-1].isalpha():
if text[0].lower() == text[-1].lower():
text.remove(text[0])
text.remove(text[-1])
return is_palindrome(text)
else:
return False
else:
text.remove(text[-1])
return is_palindrome(text)
else:
text.remove(text[0])
return is_palindrome(text)
Here are some test cases...
is_palindrome("abcde")
Results
abcde
False
is_palindrome("aabbaa")
Results
aabbaa
['b', 'b', 'a', 'a']
False
is_palindrome("aa bb cc")
Results
aa bb aa
[' ', 'b', 'b', ' ', 'a', 'a']
['b', 'b', ' ', 'a', 'a']
False
So for some reason, it always directly ends up being false.
Thoughts on how to solve this? All help will be appreciated!
text.remove(text[0]) does not do what you think it does (It removes the first occurrence of that value from the list). To remove by index use slices. You can rewrite this:
text.remove(text[0])
text.remove(text[-1])
to this:
text = text[1:-1]
Is this just for fun, or an exercise? I don't think recursion really makes sense here:
def palindrome(s):
l = len(s)
m = l // 2
return s[:m][::-1] == s[m + (l % 2):]
Then I'd just preprocess the input to remove non alphanumerics and uppers like:
s = 'aBc%$cbA'
palindrome(re.subn('\W', '', s)[0].lower())
From the Python 3 documentation:
list.remove(x)
Remove the first item from the list whose value is x. It is an error if there is no such item.
When you call text.remove(text[-1]) for the list based on the string aabbaa, the first a in the list will be removed, which is not always the desired behavior. If you want to remove the first and last elements of the list, you can use a slice as user1434070 posted. An alternate method is to use list.pop(x) to remove the element with a specified index, which is accomplished by replacing the calls to remove with the following code:
text.pop(0)
if len(text) == 0:
return True
text.pop(-1)
Note that the size of the list must be checked after the first pop in case it contained a single letter. This occurs in the deepest level of recursion when the original palindrome contains an odd number of characters.
list.remove() removes the first occurrence of an element in the list. If that element is present at the beginning and end, it doesn't matter that you selected that element by indexing the list to get the last element - it'll still remove the first one it sees. list.pop() would be more appropriate, as that removes elements based on their index rather than their value.
def is_palindrome(text):
text = list(text)
if len(text) == 0:
return True
else:
if text[0].isalpha():
if text[-1].isalpha():
if text[0].lower() == text[-1].lower():
text.pop(0)
text.pop()
return is_palindrome(text)
else:
return False
else:
text.pop()
return is_palindrome(text)
else:
text.pop(0)
return is_palindrome(text)
However, you can further improve your program by filtering out any ignored characters at the beginning:
def is_palindrome(text):
text = ''.join(filter(str.isalpha, text)).lower()
if not text:
return True
text = list(text)
if text[0].lower() == text[-1].lower():
text.pop(0)
text.pop()
return is_palindrome(text)
return False
A further optimization would be to use a default argument to note whether the string has been cleaned already, and send a slice to the recursive call:
def is_palindrome(text, cleaned=False):
if not cleaned:
text = ''.join(filter(str.isalpha, text)).lower()
if not text:
return True
if text[0] != text[-1]:
return False
return is_palindrome(text[1:-1], 1)

Categories

Resources