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))) + ']'
Related
So, I would like to convert my string input
'f(g,h(a,b),a,b(g,h))'
into the following list
['f',['g','h',['a','b'],'a','b',['g','h']]]
Essentially, I would like to replace all '(' into [ and all ')' into ].
I have unsuccessfully tried to do this recursively. I thought I would iterate through all the variables through my word and then when I hit a '(' I would create a new list and start extending the values into that newest list. If I hit a ')', I would stop extending the values into the newest list and append the newest list to the closest outer list. But I am very new to recursion, so I am struggling to think of how to do it
word='f(a,f(a))'
empty=[]
def newlist(word):
listy=[]
for i, letter in enumerate(word):
if letter=='(':
return newlist([word[i+1:]])
if letter==')':
listy.append(newlist)
else:
listy.extend(letter)
return empty.append(listy)
Assuming your input is something like this:
a = 'f,(g,h,(a,b),a,b,(g,h))'
We start by splitting it into primitive parts ("tokens"). Since your tokens are always a single symbol, this is rather easy:
tokens = list(a)
Now we need two functions to work with the list of tokens: next_token tells us which token we're about to process and pop_token marks a token as processed and removes it from the list:
def next_token():
return tokens[0] if tokens else None
def pop_token():
tokens.pop(0)
Your input consist of "items", separated by a comma. Schematically, it can be expressed as
items = item ( ',' item )*
In the python code, we first read one item and then keep reading further items while the next token is a comma:
def items():
result = [item()]
while next_token() == ',':
pop_token()
result.append(item())
return result
An "item" is either a sublist in parentheses or a letter:
def item():
return sublist() or letter()
To read a sublist, we check if the token is a '(', the use items above the read the content and finally check for the ')' and panic if it is not there:
def sublist():
if next_token() == '(':
pop_token()
result = items()
if next_token() == ')':
pop_token()
return result
raise SyntaxError()
letter simply returns the next token. You might want to add some checks here to make sure it's indeed a letter:
def letter():
result = next_token()
pop_token()
return result
You can organize the above code like this: have one function parse that accepts a string and returns a list and put all functions above inside this function:
def parse(input_string):
def items():
...
def sublist():
...
...etc
tokens = list(input_string)
return items()
Quite an interesting question, and one I originally misinterpreted. But now this solution works accordingly. Note that I have used list concatenation + operator for this solution (which you usually want to avoid) so feel free to improve upon it however you see fit.
Good luck, and I hope this helps!
# set some global values, I prefer to keep it
# as a set incase you need to add functionality
# eg if you also want {{a},b} or [ab<c>ed] to work
OPEN_PARENTHESIS = set(["("])
CLOSE_PARENTHESIS = set([")"])
SPACER = set([","])
def recursive_solution(input_str, index):
# base case A: when index exceeds or equals len(input_str)
if index >= len(input_str):
return [], index
char = input_str[index]
# base case B: when we reach a closed parenthesis stop this level of recursive depth
if char in CLOSE_PARENTHESIS:
return [], index
# do the next recursion, return it's value and the index it stops at
recur_val, recur_stop_i = recursive_solution(input_str, index + 1)
# with an open parenthesis, we want to continue the recursion after it's associated
# closed parenthesis. and also the recur_val should be within a new dimension of the list
if char in OPEN_PARENTHESIS:
continued_recur_val, continued_recur_stop_i = recursive_solution(input_str, recur_stop_i + 1)
return [recur_val] + continued_recur_val, continued_recur_stop_i
# for spacers eg "," we just ignore it
if char in SPACER:
return recur_val, recur_stop_i
# and finally with normal characters, we just extent it
return [char] + recur_val, recur_stop_i
You can get the expected answer using the following code but it's still in string format and not a list.
import re
a='(f(g,h(a,b),a,b(g,h))'
ans=[]
sub=''
def rec(i,sub):
if i>=len(a):
return sub
if a[i]=='(':
if i==0:
sub=rec(i+1,sub+'[')
else:
sub=rec(i+1,sub+',[')
elif a[i]==')':
sub=rec(i+1,sub+']')
else:
sub=rec(i+1,sub+a[i])
return sub
b=rec(0,'')
print(b)
b=re.sub(r"([a-z]+)", r"'\1'", b)
print(b,type(b))
Output
[f,[g,h,[a,b],a,b,[g,h]]
['f',['g','h',['a','b'],'a','b',['g','h']] <class 'str'>
The title is pretty self explanatory. The output only prints out the first letter of the string. I don't know what I'm doing wrong.
string = "hello"
def string_loop(string):
for x in string:
return(x)
print(string_loop(string))
output: h
Problem: Currently, your method is taking in a value string and iterating through it, however as it iterates over the first element, it will return it therefore ending the method call.
def string_loop(string):
for x in string:
return(x)
When you iterate through a string, unlike other collections (lists, dictionaries, ...) python unpacks it into an array of characters so the first element of the array is the first character which is why you only return "h".
Solution:
1) If you wanted to print each character of your string, you have two options:
a) Simply print inside your method with no return value.
def string_loop(string):
for character in string:
print(character)
string_loop("hello")
b) Add your characters to a list and print out the list returned by the method.
def string_loop(string):
my_list = list()
for character in string:
list.append(character)
print(string_loop("hello"))
2) On the other hand, if you were trying to print out every string while iterating through a list of strings, you would could iterate through the individual strings in the list
def string_loop(string_list):
for string in string_list:
print(string)
print(string_loop(["hello","world"]))
If I try the following snippets of code on a binary tree, and try to print the arr and string later, arr gives me the correct result but string is empty. Any thoughts? Is it something to do with lists being passed by reference and strings passed by value?
def post_order(root, arr = []):
if(root is not None):
post_order(root.left, arr)
post_order(root.right, arr)
arr.append(root.value)
def post_order1(root, string = ''):
if(root is not None):
post_order1(root.left, string)
post_order1(root.right, string)
string += str(root.value)
# assume I've made my binary tree
arr, string = [], ''
post_order(root, arr)
post_order1(root, string)
print arr, string
# arr holds the correct post-order sequence
# string is empty
In Python, lists are mutable and strings are immutable. This means that a list can be modified, but a string cannot. Strings can only be reassigned.
In your function, you are modifying the list using .append(), but you are only reassigning your string +=
Arr is an array, which you extend. String passed to post_order1 is an immutable object and a copy is created when updated. As a result, the original string stays unchanged.
You should rectify your code like this:
def post_order1(root, string = ''):
if not root : return string
left = post_order1(root.left, string)
right = post_order1(root.right, left)
return right + str(root.value)
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:])
I'm having problems with a homework question.
"Write a function, to_str(a), that takes an array, a, converts each of
its elements to a string (using str(a[i])) and appends all these
strings together."
This is what I have
def to_str(a):
for i in a: a.append([i])
return str(a[i])
I have no idea how to use str(a[i]), I was wondering if someone can point me to the right direction
From the docs:
str(object) -> string
Return a nice string representation of the object. If the argument is
a string, the return value is the same object.
So str(a[i]) will return a string representation of a[i], i.e. convert a[i] to a string.
You will then need to concatenates the strings for all values of i.
As for your code, I have the following comments:
i is an element of a, not an index, as you might be thinking;
you are appending elements of a to a (endlessly, I'm afraid);
a[i] can cause an exception, because, like I said, i is an element, not an index;
you need to return a concatenation of strings, not a string from one element.
Also, if using str(a[i]) is not strictly mandatory, I'd suggest to skip it as unpythonic. You don't need indexes at all for this. Examples:
''.join(str(element) for element in a)
or
''.join(map(str, a))
will return what you need. In both cases str is applied to all elements of a.
The simplest-to-understand ("beginner") way without using indexes will be
s = ''
for element in a:
s += str(element)
return s
It's a bit less efficient, though it does effectively the same thing.
Converting each element into a string is easiest to use list comprehension:
[ str(i) for i in a ]
# equivalent to
[ str(a[i]) for i in range(len(a)) ]
# equivalent to
map(str, a) # most concise, use if you want to feel incredibly clever...
So you can write the function:
def to_str2(a):
''.join([str(i) for i in a]) # concatenates the list as a list of strings
.
Your code nearly does this:
def to_str(a):
new_a = [] # rather than use the same a!
for i in a:
new_a.append(str(i)) #convert i to string before appending
return new_a
The code meeting all the task criteria is rather something like:
def to_str(a):
return reduce(lambda x, y: x + str(y), a, '')
Which does things exactly in the mentioned way: first converts them to strings, then adds to the string made from already processed elements (which at the beginning is just emty string).
EDIT: The clearer (and supported by Python 3) way is to use explicit looping through elements. It does exactly the same, clarifying at the same time how reduce() works:
def to_str(a):
result = '' # third argument of reduce()
for item in a:
result += str(item) # does what reduce() lambda argument was doing
return result
the simplest way is:
[str(i) for i in a]
if you want a function:
def to_str(a):
return [str(i) for i in a]