Python list is not passing as expected while doing recursion - python

I'm trying to solve theN-Queues
as following:
class Solution(object):
def __init__(self):
self.queues = []
def DFS(self, n, row, col, pie, na, path):
if row == n:
print 'current recursion path: ', path
self.queues.append(path)
print 'paths within the recursion: ', self.queues
return
for c in range(n):
if (c not in col) and (c + row not in pie) and (row-c not in na):
col.add(c)
pie.add(c+row)
na.add(row-c)
# self.queues.append(c)
# path += str(c)
path.append(c)
# print 'row: ', row, 'col: ', c, path
self.DFS(n, row + 1, col, pie, na, path)
col.remove(c)
pie.remove(c+row)
na.remove(row-c)
# path = path[:-1]
path.pop()
# else:
# path.pop()
return None
# print '\n'
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
col = set()
pie = set()
na = set()
print 'before recursion: ', self.queues
self.DFS(n, 0, col, pie, na, [])
print 'after recursion: ', self.queues
# return self.reslutPrint(n)
I got the following print out:
before recursion: []
current recursion path: [1, 3, 0, 2]
paths within the recursion: [[1, 3, 0, 2]]
current recursion path: [2, 0, 3, 1]
paths within the recursion: [[2, 0, 3, 1], [2, 0, 3, 1]]
after recursion: [[], []]
As you can see, the recursion is getting the correct answer for each path, however, the answer is not appended to the global variable self.queues correctly. Can you please let me know why is this happening?
I'm guessing this is due to that when I appended the list to self.queues, it's giving the address instead of the actual value to self.queues. If so, how can I fix this?
Thanks a lot.
Ian

The problem you're having isn't with self.queues, but rather how you're handling path. When you pass path it into a function, you're passing it by assignment, not its value.
Therefore, when you add path to self.queues and later do path.pop(), it is affecting anywhere path is referenced, including in self.queues.
Here's a simple example of the problem:
>>> a = []
>>> b = [1, 2, 3]
>>> a.append(b)
>>> b.pop()
>>> print(a)
[[1, 2]]
So, what's the best way to correct this? You could, as others have suggested, copy your list before appending it, with self.queues.append(path[:]). But I think a closer examination suggests another answer: pass a new path to each new level of the recursion. Rather than path.append(c), try self.DFS(n, row + 1, pie, na, path + [c]. In this way, you're taking a more functional approach of immutability (you could go one step further and enforce immutability by using an immutable data structure, such as tuple). Here's how your code would look with this approach:
def DFS(self, n, row, col, pie, na, path):
if row == n:
print 'current recursion path: ', path
self.queues.append(path)
print 'paths within the recursion: ', self.queues
return
for c in range(n):
if (c not in col) and (c + row not in pie) and (row-c not in na):
col.add(c)
pie.add(c+row)
na.add(row-c)
self.DFS(n, row + 1, col, pie, na, path + [c])
col.remove(c)
pie.remove(c+row)
na.remove(row-c)

You're right, when you append you are putting a list into self.queues, it's via the address. But that's not the issue, the issue is you later go on to modify that same object, and so emptying it also means self.queues holds a pointer to an empty list.
You need to make a copy of the path (eg by self.queues.append(path[:]) variable, and append that to queues. Or in the general case you can use python's copy moduele (either shallow or deep copy depending on your use case https://docs.python.org/2/library/copy.html)

Related

How to return a list of all possible numbers using recursion

I am learning about recursion in python currently but there is this one exercise that I simply cannot figure out in the following code I have two functions and using recursion i print all possible numbers from a given list, but for the life of me I cannot get all the possible numbers into a single list. Any tips on how to do this would really be appreciated.
def find_possible_strings(char_set, n):
k = len(char_set)
return possible_stringsRec(char_set, "", k, n)
def possible_stringsRec(char_set, prefix, k, n):
if n == 0:
print(prefix)
return
for i in range(k):
newPrefix = prefix + char_set[i]
possible_stringsRec(char_set, newPrefix, k, n - 1)
char_set = ['a','b']
possible_strings = find_possible_strings(char_set, 2)
print(possible_strings)
Just to specify I am not simply looking for a solution but rather an actual explanation of how this could be done in python if anyone would be so kind.
I am just getting the result as follows:
aa
ab
ba
bb
None
and what I want is for all those values to be stored inside a single list
so essentially:
['aa', 'ab', 'ba', 'bb']
Add a results=[] keyword argument to the possible_stringsRec function, and instead of printing out the values within the function, append to the results list:
def find_possible_strings(char_set, n):
k = len(char_set)
return possible_stringsRec(char_set, "", k, n)
def possible_stringsRec(char_set, prefix, k, n, results=[]):
if n == 0:
results.append(prefix)
return
for i in range(k):
newPrefix = prefix + char_set[i]
possible_stringsRec(char_set, newPrefix, k, n - 1)
return results
char_set = ['a','b']
possible_strings = find_possible_strings(char_set, 2)
print(possible_strings)
Output:
['aa', 'ab', 'ba', 'bb']
The explanation is pretty straightforward: the results list starts out as empty when the first function call happens, and whenever a value get appended to it, the value remains inside the list for the next function call.
This demonstrates why:
def func(lst=[]):
lst.append(1)
print(lst)
func()
func()
func()
Output:
[1]
[1, 1]
[1, 1, 1]

How to scan a list for a partial appearance using a dictionary?

I’m trying to use a dictionary to scan a list of strings to see if it appears at all within the string for example let’s say I have a dictionary of {‘C99’:1, 'C4':1} with a list of [‘C99C2C3C5’, ‘C88C4’] then the new list would be [‘1’,'1’] because ‘C99’ appears within the string ‘C99C2C3C4’ and 'C4' appears in 'C88C4'.
My current method of doing this is:
import re
dict = {'C99': 1,'C15':1}
ComponentList = ['C1C15C99', 'C15', 'C17']
def func(s):
for k, v in dict.items():
if all(i in s for i in re.findall('\w\d', k)):
return v
else:
return 0
ComponentList = [func(i) for i in ComponentList]
Output:
[1, 1, 1]
Wanted Output:
[1,1,0]
For clarification, if this is my system:
my_dict = {'C1C55C99': 1, 'C17': 1, 'C3': 1}
component_list = ['C1C15C55C99', 'C15', 'C17']
Because 'C1C55C99' appears within 'C1C15C55C99' I'd want the value to change to the dictionary value to give an output:
results = ['1','0','1']
However, this method doesn’t work when the component number gets above C9 and am hoping someone could help me on a fix, so it can work for Cx and explain why the previous method didn’t work.
Thanks Ben
From your comments here, it seems to me that the character 'C' in your component list is significant because you seem to want to differentiate between 'C11' for example and 'C1'.
BTW, I fully agree with #martineau to always use standard naming in python. CamleCasingLikeThis should only be reserved for class names, and you should use lower_case_like_this for variables in general, and not capitalized.
Let's walk through how this can be done.
my_dict = {'C99': 1, 'C15': 1, 'C1': 1}
component_list = ['C1C15C99', 'C15', 'C17']
result = []
# first convert my_dict to a list of numbers ['99', '15', '1']
elements = [element[1:] for element in my_dict.keys()]
# for every component you want to characterize
for component in component_list:
# a flag to know if we found any element in this component
found = False
# split the string by the 'C' character to get its sub element numbers
# for example 'C1C15C99'.split('C') == ['', '1', '15', '99']
for sub_elem in component.split('C'):
# make sure sub_elem is not an empty string
if sub_elem:
# check if this sub element exists in elements
if sub_elem in elements:
found = True
# exit the inner loop
break
# convert the boolean to int (either 0 or 1)
# and finally add this to the result
result.append(int(found))
print(result)
# [1, 1, 0]
So far, I've been under the presumption that my_dict can only take singular components like C1 or C6 but not composites like C12C14. From your latest comment, it appears this is not the case. Two more things are suddenly made clear: my_dict can contain a combination of components, and when checking for existence of one in another, order doesn't matter. For example, C1C2 does exist in C5C2C7C1 but C1C2 does not exist in C1 since both sub components have to present.
This is very important and it changes the problem entirely. For future reference, please make sure to exhaustively describe your problem from t he start.
my_dict = {'C99': 1, 'C15': 1, 'C1': 1, 'C1C55C99': 1, 'C99C6': 1, 'C2C4C18': 1}
component_list = ['C1C15C99', 'C15', 'C17', 'C8C6C80C99', 'C6', 'C55C2C4C18C7', 'C55C1', 'C18C4']
result = []
# first convert my_dict to a list of lists containing singular elements
elements = [element.split('C')[1:] for element in my_dict.keys()]
# elements = [['2', '4', '18'], ['99'], ['1'], ['15'], ['99', '6'], ['1', '55', '99']]
for component in component_list:
found = False
# gather the sub elements for this components
comp_elements = component.split('C')[1:]
for composite_element in elements:
element_exists = True
# check if every singular element in this element is present in component
for signular_element in composite_element:
if signular_element not in comp_elements:
element_exists = False
break
if element_exists:
found = True
break
result.append(int(found))
print(result)
# [1, 1, 0, 1, 0, 1, 1, 0]
I'm bad at one liners but it is much more simple than yours, and there was no need to use regex, just use if x in y
def func(s):
for k, v in dict.items():
if k in s:
return v
return 0
Based on the edits to your question and comments, I think I (finally) understand what it is you want to do, so here's my substantially revised answer.
I think the code shown could stand a little improvement/optimization, but would first like confirmation that it's now doing the right thing.
import re
def func(comps):
pats = [c for c in re.findall(r'\w\d+', comps)]
for k, v in my_dict.items():
if any(p in k for p in pats):
return v
return 0
# Testcases
my_dict = {'C99': 1, 'C4': 1}
components_list = ['C99C2C3C5', 'C88C4']
result = [func(comps) for comps in components_list]
print('result:', result) # -> result: [1, 1]
my_dict = {'C99': 1,'C15': 1}
components_list = ['C1C15C99', 'C15', 'C17']
result = [func(comps) for comps in components_list]
print('result:', result) # -> result: [1, 1, 0]
my_dict = {'C1C55C99': 1, 'C17': 1, 'C3': 1}
components_list = ['C1C15C55C99', 'C15', 'C17']
result = [func(comps) for comps in components_list]
print('result:', result) # -> result: [1, 0, 1]
Note: You really shouldn't name variables the same as Python built-ins, like dict, as it's confusing and can cause subtle bugs unless you're very careful (or just got lucky).
Generally I would suggest following the PEP 8 - Style Guide for Python Code, especially the Nnaming Conventions section, which would require also changing ComponentList into lowercase words separated by "_" characters—in this case, components_list would conform to the guidelines.

Replace one item in a string with one item from a list

I have a string and a list:
seq = '01202112'
l = [(0,1,0),(1,1,0)]
I would like a pythonic way of replacing each '2' with the value at the corresponding index in the list l such that I obtain two new strings:
list_seq = [01001110, 01101110]
By using .replace(), I could iterate through l, but I wondered is there a more pythonic way to get list_seq?
I might do something like this:
out = [''.join(c if c != '2' else str(next(f, c)) for c in seq) for f in map(iter, l)]
The basic idea is that we call iter to turn the tuples in l into iterators. At that point every time we call next on them, we get the next element we need to use instead of the '2'.
If this is too compact, the logic might be easier to read as a function:
def replace(seq, to_replace, fill):
fill = iter(fill)
for element in seq:
if element != to_replace:
yield element
else:
yield next(fill, element)
giving
In [32]: list(replace([1,2,3,2,2,3,1,2,4,2], to_replace=2, fill="apple"))
Out[32]: [1, 'a', 3, 'p', 'p', 3, 1, 'l', 4, 'e']
Thanks to #DanD in the comments for noting that I had assumed I'd always have enough characters to fill from! We'll follow his suggestion to keep the original characters if we run out, but modifying this approach to behave differently is straightforward and left as an exercise for the reader. :-)
[''.join([str(next(digit, 0)) if x is '2' else x for x in seq])
for digit in map(iter, l)]
I don't know if this solution is 'more pythonic' but:
def my_replace(s, c=None, *other):
return s if c is None else my_replace(s.replace('2', str(c), 1), *other)
seq = '01202112'
l = [(0,1,0),(1,1,0)]
list_req = [my_replace(seq, *x) for x in l]
seq = '01202112'
li = [(0,1,0),(1,1,0)]
def grunch(s, tu):
it = map(str,tu)
return ''.join(next(it) if c=='2' else c for c in s)
list_seq = [grunch(seq,tu) for tu in li]

pyspark: keep a function in the lambda expression

I have the following working code:
def replaceNone(row):
myList = []
row_len = len(row)
for i in range(0, row_len):
if row[i] is None:
myList.append("")
else:
myList.append(row[i])
return myList
rdd_out = rdd_in.map(lambda row : replaceNone(row))
Here row is from pyspark.sql import Row
However, it is kind of lengthy and ugly. Is it possible to avoid making the replaceNone function by writing everything in the lambda process directly? Or at least simplify replaceNone()? Thanks!
I'm not sure what your goal is. It seems like you're jsut trying to replace all the None values in each row in rdd_in with empty strings, in which case you can use a list comprehension:
rdd_out = rdd_in.map(lambda row: [r if r is not None else "" for r in row])
The first call to map will make a new list for every element in row and the list comprehension will replace all Nones with empty strings.
This worked on a trivial example (and defined map since it's not defined for a list):
def map(l, f):
return [f(r) for r in l]
l = [[1,None,2],[3,4,None],[None,5,6]]
l2 = map(l, lambda row: [i if i is not None else "" for i in row])
print(l2)
>>> [[1, '', 2], [3, 4, ''], ['', 5, 6]]

Find the index of an item in a list that starts with a user defined input

Given a list such as:
lst = ['abc123a:01234', 'abcde123a:01234', ['gfh123a:01234', 'abc123a:01234']]
is there a way of quickly returning the index of all the items which start with a user-defined string, such as 'abc'?
Currently I can only return perfect matches using:
print lst.index('abc123a:01234')
or by doing this in a number of steps by finding all the elements that start with 'abc' saving these to a new list and searching the original list for perfect matches against these.
If the only quick way is to use regex how could I still have the flexibility of a user being able to input what the match should be?
You can accomplish that using the following script/method (which I admit is quite primitive):
lst = ['abc123a:01234', 'abcde123a:01234', ['gfh123a:01234', 'abc123a:01234']]
user_in = 'abc'
def get_ind(lst, searchterm, path=None, indices=None):
if indices is None:
indices = []
if path is None:
path = []
for index, value in enumerate(lst):
if isinstance(value, list):
get_ind(value, searchterm, path + [index], indices)
elif value.startswith(searchterm):
indices.append(path + [index])
return indices
new_lst = get_ind(lst, user_in)
>>> print new_lst
[[0], [1], [2, 1]]

Categories

Resources