I am trying to split a string of arbitrary length into chunks of 3 characters. I know this question has been asked previously (How do you split a list into evenly sized chunks?), but that answer solves the problem with a list comprehension; I'm trying to solve the problem using a recursive function call, so my question is more about recursive functions calls in Python.
My function works fine until the "base case", the very last string of 3 or less characters. I am getting a TypeError: can only concatenate list (not "NoneType") to list.
Why is the base case returning None instead of a list? I explicitly create a list called final_value in the base case and return that. I even have a debugging print statement that shows me that the base case return value is of type <class 'list'>.
My code is below.
three_char_strings = []
def split3(str):
if len(str) <= 3:
final_value = []
final_value.append(str)
print('Final value: %s\nFinal value type: %s\n' % (final_value, type(final_value))) #For debugging
return final_value
else:
beginning = str[0:3]
three_char_strings.append(beginning)
remaining = str[3:]
three_char_strings + split3(remaining)
You have two problems:
You only return in the base case, so the other case will implicitly return None; and
You don't mutate three_char_strings in the base case. In fact, it's not clear why you would implement this to mutate an external list at all, as this will cause problems if you need to call it again.
You should probably have done something like:
def split3(str):
if len(str) <= 3:
return [str]
else:
beginning = str[:3]
remaining = str[3:]
return [beginning] + split3(remaining)
Which does what you want, without relying on the three_char_list list to be in-scope and empty when the function is called:
>>> split3('abcdefghijklmnopqrstuvwxyz')
['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz']
The downside of that approach is that it creates several lists. If you want one list per top-level call, you could do e.g.:
def split3(str, out=None):
if out is None:
out = []
out.append(str[:3])
if len(str) > 3:
split3(str[3:], out)
return out
If you're wondering why out=None, see "Least Astonishment" and the Mutable Default Argument.
Though the original issue was that your non-base-case didn't have a return statement (meaning it implicitly returned None), it's also instructive to see how the code could be simplified in Python
def split3(s):
return [s] if len(s) <= 3 else [s[:3]] + split3(s[3:])
Related
My question is to write a function which returns the longest string and ignores any non-strings, and if there are no strings in the input list, then it should return None.
my answer:
def longest_string(x):
for i in max(x, key=len):
if not type(i)==str:
continue
if
return max
longest_string(['cat', 'dog', 'horse'])
I'm a beginner so I have no idea where to start. Apologies if this is quite simple.
This is how i would do it:
def longest_string(x):
Strings = [i for i in x if isinstance(i, str)]
return(max(Strings, key=len)) if Strings else None
Based on your code:
def longest_string(x):
l = 0
r = None
for s in x:
if isinstance(s, str) and len(s) > l:
l = len(s)
r = s
return r
print(longest_string([None, 'cat', 1, 'dog', 'horse']))
# horse
def longest_string(items):
try:
return max([x for x in items if isinstance(x, str)], key=len)
except ValueError:
return None
def longest_string(items):
strings = (s for s in items if isinstance(s, str))
longest = max(strings, key=len) if strings else None
return longest
print(longest_string(['cat', 'dog', 'horse']))
Your syntax is wrong (second-to-last line: if with no condition) and you are returning max which you did not define manually. In actuality, max is a built-in Python function which you called a few lines above.
In addition, you are not looping through all strings, you are looping through the longest string. Your code should instead be
def longest_string(l):
strings = [item for item in l if type(item) == str]
if len(strings):
return max(strings, key=len)
return None
You're on a good way, you could iterate the list and check each item is the longest:
def longest_string(x)
# handle case of 0 strings
if len(x) == 0:
return None
current_longest = ""
# Iterate the strings
for i in x:
# Handle nonestring
if type(i) != str:
continue
# if the current string is longer than the longest, replace the string.
if len(i) > len(current_longest):
current_longest = i
# This condition handles multiple elements where none are strings and should return None.
if len(current_longest) > 0:
return current_longest
else:
return None
Since you are a beginner, I recommend you to start using python's built-in methods to sort and manage lists. Is the best when it comes to logic and leaves less room for bugs.
def longest_string(x):
x = filter(lambda obj: isinstance(obj, str), x)
longest = max(list(x), key=lambda obj: len(obj), default=None)
return longest
Nonetheless, you were in a good way. Just avoid using python´s keywords for variable names (such as max, type, list, etc.)
EDIT: I see a lot of answers using one-liner conditionals, list comprehension, etc. I think those are fantastic solutions, but for the level of programming the OP is at, my answer attempts to document each step of the process and be as readable as possible.
First of all, I would highly suggest defining the type of the x argument in your function.
For example; since I see you are passing a list, you can define the type like so:
def longest_string(x: list):
....
This not only makes it more readable for potential collaborators but helps enormously when creating docstrings and/or combined with using an IDE that shows type hints when writing functions.
Next, I highly suggest you break down your "specs" into some pseudocode, which is enormously helpful for taking things one step at a time:
returns the longest string
ignores any non-strings
if there are no strings in the input list, then it should return None.
So to elaborate on those "specifications" further, we can write:
Return the longest string from a list.
Ignore any element from the input arg x that is not of type str
if no string is present in the list, return None
From here we can proceed to writing the function.
def longest_string(x: list):
# Immediately verify the input is the expected type. if not, return None (or raise Exception)
if type(x) != list:
return None # input should always be a list
# create an empty list to add all strings to
str_list = []
# Loop through list
for element in x:
# check type. if not string, continue
if type(element) != str:
pass
# at this point in our loop the element has passed our type check, and is a string.
# add the element to our str_list
str_list.append(element)
# we should now have a list of strings
# however we should handle an edge case where a list is passed to the function that contains no strings at all, which would mean we now have an empty str_list. let's check that
if not str_list: # an empty list evaluates to False. if not str_list is basically saying "if str_list is empty"
return None
# if the program has not hit one of the return statements yet, we should now have a list of strings (or at least 1 string). you can check with a simple print statement (eg. print(str_list), print(len(str_list)) )
# now we can check for the longest string
# we can use the max() function for this operation
longest_string = max(str_list, key=len)
# return the longest string!
return longest_string
I have been tasked with trying to recreate the methods of an ArrayList type for python. I have been successful in recreating everything except for the str and the repr function, but since they behave mostly the same I only need help with one. Below is my code for the method, but I keep getting a Type Error stating "can only join an iterable"
def __str__(self):
"""Implements `str(self)`. Returns '[]' if the list is empty, else
returns `str(x)` for all values `x` in this list, separated by commas
and enclosed by square brackets. E.g., for a list containing values
1, 2 and 3, returns '[1, 2, 3]'."""
str= '['
if len(self.data)>0:
for i in range(len(self.data)):
if i ==0:
str1 = str+''.join(self[i])
else:
str1 = str+','+ ''.join(self[i])
return str1+']'
else:
return '[]'
There is just one catch to all of this, and that is that I can only call the following methods to help, though this should not be a limitation for this method. Please Help!
Methods:
lst[i] for getting and setting a value at an existing, positive index i
len(lst) to obtain the number of slots
lst.append(None) to grow the list by one slot at a %time
del lst[len(lst)-1] to delete the last slot in a list
The key is that you can use str() on the elements of the list; since your __str__() isn't calling str(self), you won't have infinite recursion.
Here is one way to do it.
#UNTESTED
def __str__(self):
"""Implements `str(self)`. Returns '[]' if the list is empty, else
returns `str(x)` for all values `x` in this list, separated by commas
and enclosed by square brackets. E.g., for a list containing values
1, 2 and 3, returns '[1, 2, 3]'."""
# Assuming that SELF implements iteration
return '[' + ', '.join(str(x) for x in self) + ']'
# Assuming that iteration isn't implemented
return '[' + ', '.join(str(self[i]) for i in range(len(self))) + ']'
I wrote this simple python code but now I have to convert this into a class structure which accepts an iterator and act as a generator to yield the tuples(instead of return it should yield). I am unable to convert it:
def func(str,size):
size1 = size
list1 = []
for i in str:
list1.append(i)
if len(list1) < size1:
return (0)
continue
if len(list) > win_size1:
list1.pop(0)
min = min(list1)
return (min)
if __name__ == '__main__':
str = [1,2,3,4,5,6]
size = [4]
res = sol2(str,size)
Also I have to get the time and space complexity. In my view time complexity looks O(n)(I am not 100% confident though) but I am unable to determine space complexity.
How can I convert this code into a class with generator and what would be the time and space complexity?
Currently the continue statements which follow the return statements make no sense - continue will never be reached. However, you can yield each tuple with yield instead of return, e.g., replace
return (None,None,None,None)
with
yield (None,None,None,None)
Now the continue statement can actually be executed, and your algorithm can return more than one tuple.
Simply changing the returns into yields makes your function a generator.
I don't see any necessity to make this a "class structure". You can create a class and make sol2() a method of that class, but it's not clear why you should want to do that.
only the window1 and window2 will ask for extra space, so I think the space complexity will be O(n).
This question already has answers here:
How can I get a reversed copy of a list (avoid a separate statement when chaining a method after .reverse)?
(11 answers)
Closed 6 years ago.
I can't figure out why my if-else statement doesn't run as expected. I've tried to create code to test for a palindromic string. My print functions show me that the reverse method works, but when I get to the comparison stage I can't get it to return True.
Here is the code:
def is_palindrome(a):
myList = []
for i in a:
myList.append(i)
print myList
new = myList.reverse()
print myList
print new
if myList == new:
return True
else:
return False
print is_palindrome("radar")
This returns False. I have also tried changing the if statement to if myList is new: but unfortunately it still returns False.
Any insight appreciated!
list.reverse() is in-place, meaning it reverses the list it is called upon but doesn't return anything.
The line print new should print None, and therefore myList == new will be False.
Instead, use [::-1] which is not in-place and returns a new, reversed list, or use an easier way to detect a palindrome, for example:
def is_palindrome(iterable):
return iterable == iterable[::-1]
I'm trying to write a recursive python function that takes in a list for example [1,2,3,4] and returns an integer 1234. Any help on how to do this
def listtoint(lst):
if lst==[]:
return 0
return lst[-1:]+clti(lst/10)
I know you can't divide the list but I would like a way to get around it
def listtoint(lst):
if lst == []:
return 0
s = ''.join([str(i) for i in lst])
return int(s)
How this works is: ''.join(some_list) takes every element of the list and concatenates them into one long string. every element of some_list here must already be a string, thus the list comprehension in the code above.
int is then used to turn the resulting string into an integer.
There should be error checking but you can deal with that. Also, this isn't recursive and doesn't need to be.
To do this recursively...
def listtoint(lst):
if lst==[]:
return 0
iPower = 10**(len(lst)-1)
return lst[0]*iPower + listtoint(lst[1:])