python function argument update - python

I am working on a function to print the list elements in reverse order using recursion. I came up with the following code
class Solution:
def __init__(self):
self.count=0
def reverseString(self, s):
def helper(s):
"""
Do not return anything, modify s in-place instead.
"""
print(s[:])
if len(s)>1:
s[0],s[len(s)-1]=s[len(s)-1],s[0]
print('s[0]',s[0])
print('s[len(s)-1]',s[len(s)-1])
helper(s[1:len(s)-1])
helper(s)
As you see, I am using print statements to debug the code. I get the following output
['h', 'e', 'l', 'p', 'o']
s[0] o
s[len(s)-1] h
['e', 'l', 'p']
s[0] p
s[len(s)-1] e
['l']
['o', 'e', 'l', 'p', 'h']
I see that my logic is working that there is something fundamental I am missing about variable update at local and global level. Can someone explain to me why I am swapping the first and last list element but my list output is not correct? I expect the output to be ['o', 'p', 'l', 'e', 'h']
On the other hand below modification seems to work fine
class Solution:
def __init__(self):
self.count=0
def reverseString(self, s):
def helper(left,right):
"""
Do not return anything, modify s in-place instead.
"""
print(s[:])
if left<right:
s[left],s[right]=s[right],s[left]
print('s[0]',s[left])
print('s[len(s)-1]',s[right])
helper(left+1,right-1)
helper(0,len(s)-1)
x=Solution()
s=["h","e","l","p","o"]
x.reverseString(s)
print(s)
['h', 'e', 'l', 'p', 'o']
s[0] o
s[len(s)-1] h
['o', 'e', 'l', 'p', 'h']
s[0] p
s[len(s)-1] e
['o', 'p', 'l', 'e', 'h']
['o', 'p', 'l', 'e', 'h']
I looked at the discussion Python inplace update of function arguments? and Immutable vs Mutable types which could possibly be related.

Your code essentially swaps two elements together and in your last line of code, you are swapping only the first and last. Your code should find a way to swap all elements not just the first and last.

I think something like the code below might do the trick.
Notice how the print is done after the recursive call, this is done on purpose, because when the call stack returns it executes all the calls in reverse order, which applies for the prints statements after the recursive calls.
def reverse_print(list):
if list: # As long as the list is not empty proceed with the recursion / print.
reverse_print(list[1:]) # Start from the next list element.
print(list[0]) # The print statement is after the recursive call, on purpose.
else: # The list is been reduced one element at a time until it reaches 0 length.
return
s = ["h", "e", "l", "p", "o"]
reverse_print(s)
When run this prints the list of strings containing a single character in reverse order:
o p l e h

Related

Cannot find glitch in program using recursion for multible nested for-loops

alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z']
endlist = []
def loopfunc(n, lis):
if n ==0:
endlist.append(lis[0]+lis[1]+lis[2]+lis[3]+lis[4])
for i in alphabet:
if n >0:
lis.append(i)
loopfunc(n-1, lis )
loopfunc(5, [])
This program is supposed to make endlist be:
endlist = [aaaaa, aaaab, aaaac, ... zzzzy, zzzzz]
But it makes it:
endlist = [aaaaa, aaaaa, aaaaa, ... , aaaaa]
The lenght is right, but it won't make different words. Can anyone help me see why?
The only thing you ever add to endlist is the first 5 elements of lis, and since you have a single lis that is shared among all the recursive calls (note that you never create a new list in this code other than the initial values for endlist and lis, so every append to lis is happening to the same list), those first 5 elements are always the a values that you appended in your first 5 recursive calls. The rest of the alphabet goes onto the end of lis and is never reached by any of your other code.
Since you want string in the end, it's a little easier just to use strings for collecting your items. This avoids the possibility of shared mutable references which is cause your issues. With that the recursion becomes pretty concise:
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def loopfunc(n, lis=""):
if n < 1:
return [lis]
res = []
for a in alphabet:
res.extend(loopfunc(n-1, lis + a))
return res
l = loopfunc(5)
print(l[0], l[1], l[-1], l[-2])
# aaaaa aaaab zzzzz zzzzy
Note that with n=5 you'll have almost 12 million combinations. If you plan on having larger n values, it may be worth rewriting this as a generator.

This code should reverse the list but only swaps first and last elements

class Solution:
def reverseString(self, s: List[str]) -> None:
if(len(s)<=1):
return
s[0],s[-1] = s[-1],s[0]
self.reverseString(s[1:-1])
this was a question on LeetCode. We have to reverse the list using recursion without using extra memory, i.e in-place.
I wrote this code but I am not sure why it is not working. For example, when s = ['h', 'e', 'l', 'l', 'o'], the output is ['o', 'e', 'l', 'l', 'h'] instead of ['o', 'l', 'l', 'e', 'h'] - it only swaps the first and last elements of the list.
The following works for me:
def reverse_inplace(char_list, step=1):
start, stop = step - 1, -step
if step == 1:
pass
elif len(char_list[start:stop]) <= 1:
return
char_list[start], char_list[stop] = char_list[stop], char_list[start]
reverse_inplace(char_list, step=step+1)
This passes the same list reference to each recursive call, and simply keeps track of how far along you are in the process with a step parameter.
reverse_inplace(list("hello")) outputs:
['o', 'l', 'l', 'e', 'h']
Kind of a fix for your concept:
def reverseString(s, n=0):
if(n==len(s)//2):
return
else:
s[n], s[-n-1]=s[-n-1], s[n]
reverseString(s, n+1)
x=list("new york")
reverseString(x)
print(''.join(x))
#outputs: kroy wen
x=list("hello")
reverseString(x)
print(''.join(x))
#outputs: olleh

String vs list membership check

So i'm wondering why this:
'alpha' in 'alphanumeric'
is True, but
list('alpha') in list('alphanumeric')
is False.
Why does x in s succeed when x is a substring of s, but x in l doesn't when x is a sublist of l?
When you use list function with any iterable, a new list object will be created with all the elements from the iterable as individual elements in the list.
In your case, strings are valid Python iterables, so
>>> list('alpha')
['a', 'l', 'p', 'h', 'a']
>>> list('alphanumeric')
['a', 'l', 'p', 'h', 'a', 'n', 'u', 'm', 'e', 'r', 'i', 'c']
So, you are effectively checking if one list is a sublist of another list.
In Python only Strings have the in operator to check if one string is part of another string. For all other collections, you can only use individual members. Quoting the documentation,
The operators in and not in test for collection membership. x in s evaluates to true if x is a member of the collection s, and false otherwise. x not in s returns the negation of x in s. The collection membership test has traditionally been bound to sequences; an object is a member of a collection if the collection is a sequence and contains an element equal to that object. However, it make sense for many other object types to support membership tests without being a sequence. In particular, dictionaries (for keys) and sets support membership testing.
For the list and tuple types, x in y is true if and only if there exists an index i such that x == y[i] is true.
For the Unicode and string types, x in y is true if and only if x is a substring of y. An equivalent test is y.find(x) != -1. Note, x and y need not be the same type; consequently, u'ab' in 'abc' will return True. Empty strings are always considered to be a substring of any other string, so "" in "abc" will return True.
For the second one you are asking if
['a', 'l', 'p', 'h', 'a'] in ['a', 'l', 'p', 'h', 'a', 'n', 'u', 'm', 'e', 'r', 'i', 'c']
and there is no sub-list in the second list only characters.
['a', 'l', 'p', 'h', 'a'] in [['a', 'l', 'p', 'h', 'a'], ['b', 'e', 't', 'a']]
would be true
lists determine membership if an item is equal to one of the list members.
strs determine whether string a is in string b if a substring of b is equal to a.
I suppose you are looking for the fact that string and list has different implementations of __contains__ magic method.
https://docs.python.org/2/reference/datamodel.html#object.contains
This is why 'alpha' in 'alphanumeric' is True, but
list('alpha') in list('alphanumeric') is False
maybe you should try issubset method.
>>> set('alpha').issubset(set('alphanumeric'))
True
although set('alpha') returns set(['a', 'p', 'l', 'h']), and set('alphanumeric'), set(['a', 'c', 'e', 'i', 'h', 'm', 'l', 'n', 'p', 'r', 'u']).
set method makes a list ignoring repetetive elements.

How do I flatten nested lists of lists in python?

I have created a function to split paths into lists of directories in python like so:
splitAllPaths = lambda path: flatten([[splitAllPaths(start), end] if start else end for (start, end) in [os.path.split(path)]])
with this helper function:
#these only work one directory deep
def flatten(list_of_lists):
return list(itertools.chain.from_iterable(list_of_lists))
The output from this function looks like so:
> splitAllPaths('./dirname/dirname2/foo.bar')
[[[['.'], 'dirname'], 'dirname2'], 'foo.bar']
now I want this as a flat list. my attempts are as follows (with the output):
> flatten(splitAllPaths('./diname/dirname2/foo.bar'))
['.', 'd', 'i', 'r', 'n', 'a', 'm', 'e', 'd', 'i', 'r', 'n', 'a', 'm', 'e', '2', 'f', 'o', 'o', '.', 'b', 'a', 'r']
and
> reduce(list.__add__, (list(mi) for mi in splitAllPaths('./dirname/dirname2/foo.bar')))
me2/foo.bar')))
[[['.'], 'dirname'], 'dirname2', 'f', 'o', 'o', '.', 'b', 'a', 'r']
How do I unfold this list correctly (I would also welcome any suggestions for how to improve my splitAllPaths function)?
This a less general answer, but it solves your original problem -- although its elegance is debatable.
The main idea is the fact that generating a list with the reversed (as in ['file', 'user', 'home', '/'] order is quite easy, so you can just create that and reverse it in the end. So it boils down to:
def split_paths(path):
def split_paths_reverse(path):
head, tail = os.path.split(path)
while head and tail:
yield tail
head, tail = os.path.split(head)
yield head
return reversed(tuple(split_paths_reverse(path)))
Example:
test = '/home/user/file.txt'
print(list(split_paths(test)))
['/', 'home', 'user', 'file.txt']
You could also avoid the explicit reversing part by putting each element in a stack and then removing them, but that's up to you.
Sortherst way that comes in mind would be:
listoflists = [[[['.'], 'dirname'], 'dirname2'], 'foo.bar']
str(listoflists).translate(None,"[]'").split(',')
I solved this by writing a (non-general) foldr. I think better, more practical solutions are provided by #L3viathan in the comments.
attempt = lambda list: attempt(list[0] + list[1:]) if len(list[0]) > 1 else list[0] + list[1:]
Output
> attempt([[[['.'], 'dirname'], 'dirname2'], 'foo.bar'])
['.', 'dirname', 'dirname2', 'foo.bar']
I've also now written it in terms of a general foldr1
> foldr1 = lambda func, list: foldr1(func, func(list[0], list[1:])) if len(list[0]) > 1 else func(list[0], list[1:])
> foldr1(list.__add__, [[[['.'], 'dirname'], 'dirname2'], 'foo.bar'])
['.', 'dirname', 'dirname2', 'foo.bar']
NOTE: Could someone more familiar than me confirm that this is a foldr and not a foldl (I often get them confused).

What i did wrong in my python function?

def make_str_from_row(board, row_index):
''' (list of list of str, int) -> str
Return the characters from the row of the board with index row_index
as a single string.
>>> make_str_from_row([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 0)
'ANTT'
'''
for i in range(len(board)):
i = row_index
print(board[i])
This prints ['A', 'N', 'T', 'T']
How do I print it like this 'ANTT' instead?
Well, you got what you told to print!
board is a list of list of strs, so board[i] must be a list of strs, and when you write print(board[i]), you get a list!
You may need to write this:
print(''.join(board[i]))
You could simplify that a whole lot by using
>>> def make_str_from_row(board, row_index):
... print repr(''.join(board[row_index]))
...
>>> make_str_from_row([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 0)
'ANTT'
The reason you get that output is because you print a list since the elements of board are lists. By using join, you get a string.
Also, I don't understand why you use a loop if are going to change the index you loop over.
I think this was what you were trying to do:
def make_str_from_row(board, row_index):
''' (list of list of str, int) -> str
Return the characters from the row of the board with index row_index
as a single string.
>>> make_str_from_row([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 0)
'ANTT'
'''
for cell in board[row_index]:
print cell,

Categories