Python recursion doesn't yield desired result - python

I am trying to find the length of the string with out using the inbuilt len() function. Here's my python code:
increment = -1
def lenRecur(aStr):
'''
aStr: a string
returns: int, the length of aStr
'''
global increment
if aStr == '':
return increment
else:
increment += 1
return lenRecur (aStr[increment:])
print lenRecur ("abcdefq")
The result I am expecting is 7 but what I got was 4.What I realized was when the increment became 2, the value pass to the lenRecur (aStr[increment:]) was "defq". Which means aStr[2:] is evaluated as "defq" instead of "cdefq".
Why this is happening?

Your function should not depend on external variables.
def lenRecur(aStr):
'''
aStr: a string
returns: int, the length of aStr
'''
if aStr == '':
return 0
else:
return 1 + lenRecur(aStr[1:])
print lenRecur("abcdefq")

A common idiom is to use a default argument:
>>> def l(s, c=0): return l(s[1:], c+1) if s else c
This kind of solution works with anything that can be sliced
>>> l('pip')
3
>>> l([1,2,3])
3
>>> l('')
0
>>> l([])
0
>>>

As an other option, you could write this:
def lenRecur(aStr):
return _lenRecur(aStr, 0)
def _lenRecur(aStr, acc):
if not aStr:
return acc
return _lenRecur(aStr[1:], acc+1)
A noticed by #gboffi in his answer, it is commonly accepted in Python to use a default argument instead of using of an helper function:
def lenRecur(aStr, acc = 0):
if not aStr:
return acc
return lenRecur(aStr[1:], acc+1)
The choice of one form or the other will depend how much you want/don't want to allow the called to set the initial accumulator value to something else than 0.
Anyway, the interesting point here is in using an accumulator as the second parameter. That way, you have proper tail recursion. Unfortunately, this is not properly optimized by Python. But this is a good habit as many other languages have such optimization. And this will be a required skill if you switch someday to functional programing language.

Related

Python classes and definitions

Here is my python code:
class Solution():
def isPalindrome(self):
return str(self.x) == str(self.x)[::-1]
s1 = Solution()
s1.x = 121
s1.isPalindrome()
It checks to see if the input is a palindrome. I want to create a new object that has the x value 121 and when I execute the isPalindrom function, I want it to return either a true or false boolean answer.
Currently when I run this program, nothing gets outputted. I am a bit lost as to where to go from here, would appreciate help.
Just print out the return value of isPalindrome(), because if you have a line with only a return value (this case being a boolean), the compiler won't know what to do with it.
class Solution():
def isPalindrome(self):
return str(self.x) == str(self.x)[::-1]
s1 = Solution()
s1.x = 121
print(s1.isPalindrome())
You're not telling the program to print anything. Try using print to make it reveal the answer.
Along with printing results we can also make class more pythonic.
class Solution:
def __init__(self):
self.input = None
def is_palindrome(self):
if isinstance(self.input, str):
return self.input == self.input[::-1]
print("Error: Expects str input")
return False # or leave blank to return None
s1 = Solution()
print(s1.is_palindrome())
s1.input = "121"
print(s1.is_palindrome())
output
Error: Expects str input
False
True
The main idea here is divide number. let's take number 122. First of all you need store it in a variable, in this case r_num. While loop is used and the last digit of the number is obtained by using the modulus operator %. The last digit 2 is then stored at the one’s place, second last at the ten’s place and so on. The last digit is then removed by truly dividing the number with 10, here we use //. And lastly the reverse of the number is then compared with the integer value stored in the temporary variable tmp if both are equal, the number is a palindrome, otherwise it is not a palindrome.
def ispalindrom(x):
r_num = 0
tmp = x
while tmp > 0:
r_num = (r_num * 10) + tmp % 10
tmp = tmp // 10
if x == r_num:
return True
return False

Trying to learn how loops work, what is going wrong?

The question is :
Given a string, return a string where for every char in the original, there are two chars.
This is my attempt:
def double_char(str):
n = 0
for x in range(0, len(str)):
return 2*str[n]
n = n+1
When I run it, it only returns 2 versions of the first letter and doesn't loop properly. So for double_char(Hello) it just returns HH.
What is going wrong? Thanks in advance for any help, sorry for the really beginner question.
The return is causing your function to return in the first iteration so it just returns 2 of the first letter.
What you may have intended to write was something like
def double_char(s):
n = 0
r = ''
for x in range(0, len(s)):
r += 2*s[n]
n = n+1
return r
Building a string incrementally that is just 2 of each character.
A neater refactor of that function (without duplicating the other answer by using a comprehension) is
def double_char(s):
r = ''
for c in s:
r += 2*c
return r
You also should not use str as a variable name. It is a built in type and you are hiding that by defining a variable called str.
return returns control to the caller once reached, thus exiting your for loop prematurely.
Here's a simpler way to do that with str.join:
def double_char(s):
return ''.join(i*2 for i in s)
>>> s = 'Hello'
>>> double_char(s)
'HHeelllloo'
Do not use str as name to avoid shadowing the builtin str function.
Here is a different way to solving the question.
def double_char(str):
new_str = ""
for i in range(len(str)):
new_str += (str[i]*2)
return new_str
double_char('Hello')
'HHeelllloo'
def double_char(str):
string = ''
for i in range(len(str)):
string += str[i] * 2
i += 1
return string

Writing filter() function but getting typeError

my code consists of me recreating the function 'filter()' and using it with a function to filter words longer than 5 characters. It worked with the actual function filter when I tried it btw...I'm using python 3+
def filter1(fn, a):
i = 0
while i != len(a):
u = i - 1
a[i] = fn(a[i], a[u])
i += 1
return a
def filter_long_words(l):
if len[l] > 5:
return [l]
listered = ['blue', 'hdfdhsf', 'dsfjbdsf', 'jole']
print(list(filter1(filter_long_words, listered)))
getting error
TypeError: filter_long_words() takes 1 positional argument but 2 were given
You are passing two parameters to fn (which refers to filter_long_words) here:
a[i] = fn(a[i], a[u])
But filter_long_words only accepts one parameter.
Notes:
You can loop through lists using for item in my_list, or if you want index as well for index, item in enumerate(my_list).
I think you might get an IndexError since u will be -1 in the first round of your loop.
The filter function can also be expressed as a list comprehension: (item for item in listered if filter_long_words(item))
My version of filter would look like this, if I have to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
for item in sequence:
if fn(item):
yield item
Since you have stated that you are using Python 3, this returns a generator instead of a list. If you want it to return a list:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
acc = []
for item in sequence:
if fn(item):
acc.append(item)
return acc
If you don't need to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
return (item for item in sequence if fn(item))
Your're calling fn with 2 parameters in filter1(fn, a), and since you've passed filter_long_words() to filter1 as fn, that triggers the error.
But there's more weird stuff:
I don't understand the magick of filter1 or what you were trying to
accomplish, but it seems to me that you don't have a clear idea what to do.
But if you want to mimic (somehow) how filter works, you have to return a
list which contains only items for which the fn function returns true. When
you know this, you can rewrite it - here are a few suggestions for rewrite
# explicit, inefficient and long, but straightforward version:
def filter1(fn, a):
new_list = []
for item in a:
if fn(item):
new_list.append(item):
return new_list
# shorter version using list comprehensions:
def filter1(fn, a):
return [item for item in a if fn(item)]
The filter_long_words function is wrong too - it should return True or
False. The only reason why it could work is because any non-empty list is
treated as True by python and default return value of a function is None,
which translates to False. But it's confusing and syntactically wrong to use
len[l] - the proper usage is len(l).
There are a few suggestions for rewrite, which all returns explicit boolean
values:
# unnecessary long, but self-explanatory:
def filter_long_words(l):
if len(l) > 5:
return True
else
return False
# short variant
def filter_long_words(l):
return len(l) > 5
You are calling "filter_long_words" with 2 parameter => fn(a[i], a[u]) also there is an error
def filter_long_words(l):
if **len[l]** > 5:
return [l]
len is builtin method it should be len(l)

How to count the number of letters in a string with a list of sample?

value = 'bcdjbcdscv'
value = 'bcdvfdvdfvvdfvv'
value = 'bcvfdvdfvcdjbcdscv'
def count_letters(word, char):
count = 0
for c in word:
if char == c:
count += 1
return count
How to count the number of letters in a string with a list of sample? I get nothing in my python shell when I wrote the above code in my python file.
There is a built-in method for this:
value.count('c')
functions need to be called, and the return values need to be printed to the stdout:
In [984]: value = 'bcvfdvdfvcdjbcdscv'
In [985]: count_letters(value, 'b')
Out[985]: 2
In [987]: ds=count_letters(value, 'd') #if you assign the return value to some variable, print it out:
In [988]: print ds
4
EDIT:
On calculating the length of the string, use python builtin function len:
In [1024]: s='abcdefghij'
In [1025]: len(s)
Out[1025]: 10
You'd better google it with some keywords like "python get length of a string" before you ask on SO, it's much time saving :)
EDIT2:
How to calculate the length of several strings with one function call?
use var-positional parameter *args, which accepts an arbitrary sequence of positional arguments:
In [1048]: def get_lengths(*args):
...: return [len(i) for i in args]
In [1049]: get_lengths('abcd', 'efg', '1234567')
Out[1049]: [4, 3, 7]
First you should probably look at correct indenting and only send in value. Also value is being overwritten so the last one will be the actual reference.
Second you need to call the function that you have defined.
#value = 'bcdjbcdscv'
#value = 'bcdvfdvdfvvdfvv'
value = 'bcvfdvdfvcdjbcdscv'
def count_letters(word, char):
count = 0
for c in word:
if char == c:
count += 1
return count
x = count_letters(value, 'b')
print x
# 2
This should produce the result you are looking for. You could also just call:
print value.count('b')
# 2
In python, there is a built-in method to do this. Simply type:
value = 'bcdjbcdscv'
value.count('c')

Keeping count in recursive function

I'm trying to figure out how to write a recursive function (with only one parameter) that returns the number of times the substring “ou” appears in the string. Where I'm confused at is that I'm not allowed to use any built-in string functions other than len, or the string operators [] and [:] for indexing and splicing. So I can't use the find built-in find function
I remember seeing something like this, but it uses two parameters and it also uses the find() method
def count_it(target, key):
index = target.find(key)
if index >= 0:
return 1 + count_it(target[index+len(key):], key)
else:
return 0
Very inefficient, but should work:
def count_it(target):
if len(target) < 2:
return 0
else:
return (target[:2] == 'ou') + count_it(target[1:])
See it working online: ideone
It's basically the same idea as the code you posted, except that it moves only one character at a time through the string instead of using find to jump ahead to the next match.
Try this, it works for the general case (any value of key, not only 'ou'):
def count_it(target, key):
if len(target) < len(key):
return 0
found = True
for i in xrange(len(key)):
if target[i] != key[i]:
found = False
break
if found:
return 1 + count_it(target[len(key):], key)
else:
return count_it(target[1:], key)

Categories