make a linear performing recursive palindrome function - python

So I have to build a function that checks if something is a palindrome, but it has to be recursive and it has to be linear and it has to ignore punctuations. I can't use any imports and my teacher told me that using the translation function isn't efficient enough. I'm trying to re-engineer it but iI'm very much stumped.
def isPalindrome(word):
newWrd = word.translate(None, "., ").lower()
if len(newWrd) <= 1:
return True
elif newWrd[0] != newWrd [-1]:
return False
else:
return isPalindrome(newWrd[1:-1])

The concept and motivation are simple:
Check the first and last character of the current string.
If they match, repeat the previous operation, but only take the slice of the string that you haven't processed yet.
If they don't match, return False.
Strings of length 1 are palindromic by default.
Here's what I would come up with. The below assumes a unified case and that all punctuation is stripped out of the word, but if you wish to do that in this method, I leave that as an exercise for the reader.
def palindrome(word):
if not word:
return False
if len(word) == 1:
return True
else:
if word[0] == word[-1]:
return palindrome(word[1:-1])
else:
return False

Normally, you should import string and use string.punctuation, but since you don't want to import anything, you can just hard-copy it to your code.
Since you already have the isPalindrome() recursive function, you can write a wrapper function, isPalindromeWrapper(), which will take the input, process it (remove the punctuations) and then call the isPalindrome() function.
punctuations = '!"#$%&\'()*+,-./:;<=>?#[\\]^_`{|}~'
def removePunctuations(word):
return ''.join( [ c for c in word if c not in punctuations ] )
def isPalindrome(word):
print('isPalindrome({}) called.'.format(word))
if len(word) < 2:
return True
if word[0] == word[-1]:
return isPalindrome(word[1:-1])
return False
def isPalindromeWrapper(word):
word = removePunctuations(word)
return isPalindrome(word)
print isPalindromeWrapper('ab,c!b?a')
Output:
isPalindrome(abcba) called.
isPalindrome(bcb) called.
isPalindrome(c) called.
True

Related

Can someone tell me why after returning a boolean of False my code goes backwards and chooses True also

Can someone tell me why after returning a boolean of False my code goes backwards and chooses True also even though the False was the right output I see this happening in the debugger, I only found this happening with this specific example. So the function of my code is to take a pattern from user and see if it matches the word given by the user, the format of a pattern is "?ead" which would match with the word lead, the question mark is just a placeholder for any character so it can be anything but every other character has to be the same.
n = 0
def match(pattern, word):
global n
if n != len(word): # setting a base case for the recurrsion so it stops at the end of the word
if pattern == "" and word == "":
pass
elif pattern == '' or word == '':
pass
elif len(word) != len(pattern):
return False
if word[n:n+1] == pattern[n:n+1] or pattern[n:n+1] == '?': # goes through each character and compares them for each
n = n +1
match(pattern, word)
print("same")
return True # This is where the code goes back to and returns true after already returning false down below in the else statement.
else:
print("not same")
return False
match("?ut","cat")
The pattern my code fails for "?ut" and the word is "cat", the code sees that the first character is fine since "?" is just a placeholder and it also sees that the second term does not match so it should evaluate to false which it does but then immediately exits and evaluates to true and all this needs to be done with recursion and no loops.
You are trying to do recursion here. It can be done like this:
def match(pattern, word):
#Little debug for you to see what is happening
print (pattern, word)
if len(pattern) != len(word):
return False
if not word and not pattern:
# last round with empty strings, all same this far
return True
if pattern[0] != word[0] and pattern[0] != "?":
return False
# reduce both strings and go to next round
return match(pattern[1:], word[1:])
So you reduce both pattern and word and if you find that they don't match return false. If you get to the end, return True.
You do not return the result of match but return True. You should simplify your cases.
if word[n:n+1] == pattern[n:n+1] or pattern[n:n+1] == '?':
n = n +1
match(pattern, word) # this can return True OR False
print("same")
return True # You always return True
# return match(pattern, word) instead
There is no need for a global "counter" - if you recurse, shorten your pattern/word until nothing is left:
def match(pattern, word):
if len(pattern) != len(word):
return False
# done all I can do
if pattern == "" and word == "":
return True
# no comparison if pattern is ?
if pattern[0] == "?":
# recurse with 1 shorter pattern/word
return match(pattern[1:], word[1:])
# same letters
elif pattern[0] == word[0]:
# recurse with 1 shorter pattern/word
return match(pattern[1:], word[1:])
# different
return False
for pat,wrd in [("?ut","cat"), ("ok","ok"), ("?k","ok"),
("o?","ok"), ("",""), ("tooShort","veryLongWord")]:
print(pat,wrd, "=>", "same" if match(pat,wrd) else "different")
Output:
?ut cat => different
ok ok => same
?k ok => same
o? ok => same
=> same
tooShort veryLongWord => different
Or use the "short" version that does not recurse:
def match(pattern, word):
return len(pattern)==len(word) and all(
(p==w or p == "?" for p,w in zip(pattern, word)))
You should return the result you get back from recursion. Currently you ignore it.
There are some other issues:
Don't use global variables. If you make another call to match, then n will still be the value it had after the first call, and so you'll get an unreliable result from the second call. Instead make it an argument to your function. You can give it a default value.
The condition if pattern == "" and word == "": can never be true, since you already had assured that n != len(word), and as n starts with 0, that if condition would have been false if word was the empty string.
When you pass in the first if blocks, the other code below is still executed, and so you'd make an invalid index reference in the string.
Adding another condition where you delimit strings with single quotes instead of double quotes is not changing anything. You still compare the same strings.
It is overkill to compare slices of one character. Just retrieve the single character you want to compare. So word[n] instead of word[n:n+1]
Don't print the result inside the function. Print at the caller's side.
Correction:
def match(pattern, word, n=0):
if len(word) != len(pattern):
return False
if n >= len(word):
return True
if word[n] != pattern[n] and pattern[n] != '?':
return False
return match(pattern, word, n + 1)
print(match("?ut","cat"))
Not your question, but this kind of recursion is a case of tail recursion, and so it could be written with simple iteration:
def match(pattern, word):
if len(word) != len(pattern):
return False
for i, pat in enumerate(pattern):
if word[i] != pat and pat != '?':
return False
return True
When you are confortable with comprehension syntax you can write that also like this:
def match(pattern, word):
return len(word) == len(pattern) and all(
pat in ('?', ch) for ch, pat in zip(word, pattern)
)

Checking if string is in a list of strings using loop

I have a function that checks if a given string is in a list of strings using a for and while loop. I’m not supposed to use the ‘in’ operator. Here’s my code using a for loop:
def word_in_list(words, word):
for strings in words:
if len(words) > 0 and strings == word:
return True
else:
return False
However, it is not returning True unless the single string is the first element of the list. The list should return False in case the list is empty. And how can I solve the same problem using a while loop (and without 'in' operator)?
Don’t return False the moment you find one mismatch, return False when you are done checking all possibilities and not finding any match:
def word_in_list(words, word):
for strings in words:
if strings == word:
return True
return False
Also, no need to check the length of list every time, if it's zero, you don't run the loop at all and directly return False.
Your code is wrong because of the else statement. Your function must return False only after checking the whole list, not just the first element. Whenever a function gets to a "return" instruction, it stops, so it just checked the first one. This is the correct solution:
def word_in_list(words, word):
i = 0
while i < len(words):
if words[i] == word:
return True
i += 1
return False
simly use this code
def word_in_list(words, word):
if word in words:
return True
else
return False
Your else block kicks-in without finishing the iteration on complete list.
def word_in_list(list_of_words, word_to_search):
found = False
for word in list_of_words:
if word == word_to_search:
found = True
break # breaks iff the word is found
return found
Any particular reason you are adamant not to use the 'in' operator?
Also, please be careful about the indentation in the code you paste.

Two simple if-else codes in Python, should they not return the same value? Why each one returns something else

Two simple if-else codes in Python, should they not return the same value? Why each one returns something else.
def letter_check(word, letter):
for i in word:
if i == letter:
return True
return False
# This returns True
print(letter_check("strawberry", "a"))
# Same function?
def letter_check(word, letter):
for i in word:
if i == letter:
return True
else:
return False
# This returns False
print(letter_check("strawberry", "a"))
They are not the same. The first def will return False only if none of the chars in word is equal to letter.
The second one checks only the first char in word and return True if they are equals or False if they aren't.
There is no need to loop by the way, use in keyword
def letter_check(word, letter):
return letter in word
When you call letter_check("strawberry", "a") the 2nd time, the function returns False because the first letter of strawberry is 's' and not 'a'.

Check python function determine isogram from codewars

An isogram is a word that has no repeating letters, consecutive or non-consecutive. Implement a function that determines whether a string that contains only letters is an isogram. Assume the empty string is an isogram. Ignore letter case.
is_isogram("Dermatoglyphics" ) == true
is_isogram("aba" ) == false
is_isogram("moOse" ) == false # -- ignore letter case
Here is my code:
def is_isogram(string):
string = string.lower()
for char in string:
if string.count(char) > 1:
return False
else:
return True
And when I tried to run the test code Test.assert_equals(is_isogram("moOse"), False, "same chars may not be same case" ) It failed, but I thought I did convert everything into lowercase. Can someone help?
Try this:
def is_isogram(string):
string = string.lower()
for char in string:
if string.count(char) > 1:
return False
return True
In your code when is_isogram("moose") is called, it will see that the first character's ('m') count is not greater than 1. So it will return True. Once it hits the return statement, it will stop the execution for the rest string. So you should really write return True only after for-loop to make sure that the function checks for the whole string.
If however, at any point, it finds a character's count to be greater than 1, then it will simply return False and stop executing because there's no point of checking any more when one point is found where condition does not hold.
How about using sets? Casting the string into a set will drop the duplicate characters, causing isograms to return as True, as the length of the set won't differ from the length of the original string:
def is_isogram(s):
s = s.lower()
return len(set(s)) == len(s)
print is_isogram("Dermatoglyphics")
print is_isogram("aba")
print is_isogram("moOse")
print is_isogram("")
This outputs:
True
False
False
True
Try this :
def is_isogram(s):
string = s.lower()
if len(s) == len(set(string)):
return True
return False
Try this out:
def is_isogram(string):
return len(string) == len(set(string.lower()))
"Implement a function that determines whether a string that contains only letters is an isogram."
By using sets, you can create unique elements. So if there are any repeating numbers, it will only select one. By calling len() on these strings, you can compare the length to the original.
Sorry if I explained it poorly. I am working on this.
let us define an isogram well:
according to wikipedia An Isogram is a word in which no letter occurs more than once.
check here for more about an isogram
just remind letter
I write this code and it works for me :
def is_isogram(argument):
print(len(argument))
if isinstance(argument,str):
valeur=argument.lower()
if not argument:
return False
else:
for char in valeur:
if valeur.count(char)>1 or not char.isalpha():
return False
return True
else:
raise TypeError("need a string ")
NB: the hidden test is the fact that you must check if the char in the string is a alpha character a-z, when i add this it pass all the hiddens tests
up vote if this help
I reckon this might not be the best solution in terms of maximizing memory space and time. This answer is just for intuition purposes using a dictionary and two for loops:
def is_isogram(string):
#your code here
#create an empty dictionary
m={}
#loop through the string and check for repeating characters
for char in string:
#make all characters lower case to ignore case variations
char = char.lower()
if char in m:
m[char] += 1
else:
m[char] = 1
#loop through dictionary and get value counts.
for j, v in m.items():
#if there is a letter/character with a count > 1 return False
if v > 1:
return False
#Notice the scope of the "return True" command. It is outside.
return True

Why doesn't my python algorithm for checking palindromes work?

So I was doing exercise 6 from chapter 7 from Think Python and it stroke me as odd that this piece of code I wrote as a solution didn't work:
def is_palindrome(word):
if len(word) <= 1:
return True
elif first(word) == last(word):
is_palindrome(middle(word))
else:
return False
It doesn't enter any recursion and I don't know why. It returns None for words longer than 1 character of length. Why is this? When the length of the word reaches 1 or less it should return True!
Why doesn't it work?
PS: Here are first, last and middle deffinitions:
def first(word):
return word[0]
def last(word):
return word[-1]
def middle(word):
return word[1:-1]
You're missing a return:
def is_palindrome(word):
if len(word) <= 1:
return True
elif first(word) == last(word):
return is_palindrome(middle(word)) # <--
else:
return False
So your current snippet is returning None once the elif block is entered, since you don't have an explicit return statement there. In other words, you do compute is_palindrome(middle(word)), but you do nothing with the result.
Maybe working through a simple example would help. Consider calling the original function with an argument of 'aba':
function called
word is now 'aba'
len(word) <= 1 is False, if-body not entered.
first(word) == last(word) is True, elif-body entered:
function called recursively:
word is now 'b'
len(word) <= 1 is True, if-body entered:
True returned
Return value of recursive call discarded (since we have no return in elif)
None returned
Add return
return is_palindrome(middle(word))
I think you are missing to return the method in 5th line of your function.
return is_palindrome(middle(word))
should be there instead of
is_palindrome(middle(word))

Categories