I am working through the following exercise: I am implementing a class that represents sorted lists of basic types.
Currently:
class SortedList():
def __init__(self, input_list):
self.input_list= input_list
def add(self,value):
self.input_list.append(value)
return self.input_list
def concat(self):
return
def __repr__(self):
self.input_list.sort()
return str(self.input_list)
I make the following calls:
l1= SortedList(['z','l','a'])
print(l1)
l1.add('b')
print(l1)
l2= SortedList(['q','g'])
l3= l1.cocat(l2)
print(l3)
Everything behaves as expected until the l3 definition, since unsure how to define this type of function x.function(y) within a class.
The desired output from the last print statement is ['a','b','g','l','q','z']
You can use the + operator on lists which extends a list with another list, and then return a new instance of SortedList when concat() is called.
class SortedList:
def __init__(self, input_list):
self.input_list = sorted(input_list)
def add(self, value):
self.input_list.append(value)
self.input_list.sort()
return self.input_list
def concat(self, other):
merged = self.input_list + other.input_list
return SortedList(merged)
def __repr__(self):
return str(self.input_list)
l1 = SortedList(["z", "l", "a"])
print(l1)
# ['a', 'l', 'z']
print(l1.add("b"))
# ['a', 'b', 'l', 'z']
l2 = SortedList(["q", "g"])
l3 = l1.concat(l2)
print(l3)
# ['a', 'b', 'g', 'l', 'q', 'z']
Related
I'm working out how to return a list of values from a depth-first traversal of a binary tree. Specifically, I'm trying to do so with a recursive function.
After playing around a bit I got this code to properly return the list of expected values, but using a try/finally block in order to pull it off seems clunky to me.
def depth_first_recursive(root, node_list: list):
try:
if not root:
return
else:
node_list.append(root.val)
depth_first_recursive(root.left, node_list)
depth_first_recursive(root.right, node_list)
finally:
return node_list
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
values = []
if __name__ == '__main__':
a = Node('a')
b = Node('b')
c = Node('c')
d = Node('d')
e = Node('e')
f = Node('f')
a.left = b
a.right = c
b.left = d
b.right = e
c.right = f
nodelist = []
print(depth_first_recursive(a, node_list=nodelist))
returns ['a', 'b', 'd', 'e', 'c', 'f', ]
Is there a better way to pull this off in Python?
Simplified yours, much more efficient than all the copying of the others. Yours takes O(n) time, theirs is O(n²).
def depth_first_recursive(root, node_list: list):
if root:
node_list.append(root.val)
depth_first_recursive(root.left, node_list)
depth_first_recursive(root.right, node_list)
return node_list
I have applied the same logic as yours but tried to make code compact:
def depth_first_recursive(root, node_list: list):
if root is None: return []
return [root.val]+depth_first_recursive(root.left, node_list) + depth_first_recursive(root.right, node_list)
Output:
['a', 'b', 'd', 'e', 'c', 'f']
Don't need to maintain a node_list, just have more faith in the structure of your recursion.
def depth_first_recursive(root):
if root is None:
return []
# Terminal case when reaching bottom of tree
elif not root.left and not root.right:
return [root.val]
# Get current value and continue to append list
else:
return [root.val] + depth_first_recursive(root.left) + depth_first_recursive(root.right)
class Hat:
def __init__(self, **kwargs):
self.contents = []
for balltype in kwargs.keys():
for ballnum in range(kwargs[balltype]):
self.contents += balltype
hattrial = Hat(red = 1, blue = 2)
print(hattrial.contents)
I'm trying top create a list that contains the keys from the input argument dictionary, but instead of simply adding the string entry I get:
['r', 'e', 'd', 'b', 'l', 'u', 'e', 'b', 'l', 'u', 'e']
Instead of:
['red', 'blue', 'blue']
Where red occurs once and blue occurs twice. I've tried a few different solutions short of just manipulating the array afterwards such as the attempt below but nothing I've done has changed the output. Surely there's an elegant solution that doesn't require me sticking characters back together?
end = len(balltype)
self.contents += balltype[0:end]
self.contents += balltype
Using append
class Hat:
def __init__(self, **kwargs):
self.contents = []
for balltype in kwargs.keys():
for ballnum in range(kwargs[balltype]):
self.contents.append(balltype)
hattrial = Hat(red = 1, blue = 2)
print(hattrial.contents)
Be careful with the += operator in lists
This also works, try to understand why it appends correctly here with +=
class Hat:
def __init__(self, **kwargs):
self.contents = []
for balltype in kwargs.keys():
self.contents += kwargs[balltype] * [balltype]
hattrial = Hat(red = 1, blue = 2)
print(hattrial.contents)
Basically, the problem with your code can be reduced to the following:
a = []
a += "hello"
a
['h', 'e', 'l', 'l', 'o']
I am using Yen's Algorithm (Wikipedia) to find k shortest paths in a graph. In the example below, my graph is a dictionary where each node is a key, with its value being the neighbors. Map() from dotmap simply allows for dictionaries to be converted into an object where keys can be accessed with dot notation. I want to find the four shortest paths in descending order from A to F where every edge has equal weight. The first two are ties (A > B > D > F) and (A > E > D > F), and the next two are (A > B > C > G > F) and finally (A > B > D > C > G > F). It is possible that my implementation of Dijkstra's (called AStar despite having no heuristic) is flawed because it is returning an empty list when no path is found. How can I have my code only pick the valid paths? Currently it returns [['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], [], []] -- it should return [['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], ['A', 'B', 'C', 'G', 'F'], ['A', 'B', 'D', 'C', 'G', 'F']] which are the shortest paths.
import copy
import heapq
from dotmap import Map
from itertools import count
graph = {
'A': ['B', 'E'],
'B': ['C', 'D'],
'C': ['G'],
'D': ['C', 'F'],
'E': ['D'],
'F': [],
'G': ['F']
}
class PriorityQueue:
def __init__(self):
self.elements = []
self._counter = count()
def empty(self):
return len(self.elements) == 0
def put(self, item, priority):
heapq.heappush(self.elements, (priority, item,))
def get(self):
return heapq.heappop(self.elements)[1]
class AStar:
def __init__(self, graph, start, goals=[]):
self.graph = graph
self.start = start
self.frontier = PriorityQueue()
self.frontier.put(start, 0)
self.previous = {}
self.previous[start] = None
self.costs = {}
self.costs[start] = 0
self.final_path = None
self.goals = goals
self.goal = None
def search(self):
graph = self.graph
frontier = self.frontier
goals = self.goals
costs = self.costs
while not frontier.empty():
state = frontier.get()
if state in goals:
cost = self.costs[state]
self.goal = state
self.final_path = self.trace_path()
return Map({'path': self.final_path, 'cost': cost})
for next_state in graph[state]:
new_cost = costs[state] + 1
if next_state not in costs or new_cost < costs[next_state]:
costs[next_state] = new_cost
priority = new_cost
frontier.put(next_state, priority)
self.previous[next_state] = state
# No path found
return Map({'path': [], 'cost': 0})
def trace_path(self):
current = self.goal
path = []
while current != self.start:
path.append(current)
current = self.previous[current]
path.append(self.start)
path.reverse()
return path
def YenKSP(graph, source, sink, k_paths):
graph_clone = copy.deepcopy(graph)
A = [AStar(graph, source, sink).search().path]
B = []
for k in range(1, k_paths):
for i in range(len(A[-1]) - 1):
spur_node = A[-1][i]
root_path = A[-1][:i+1]
for path in A:
if len(path) > i and root_path == path[:i+1]:
graph_clone[path[i]].remove(path[i+1])
result = AStar(graph_clone, spur_node, sink).search()
spur_path = result.path
total_path = root_path[:-1] + spur_path
spur_cost = AStar(graph_clone, source, spur_node).search().cost
B.append(Map({'path': total_path, 'cost': result.cost + spur_cost}))
graph_clone = copy.deepcopy(graph)
if len(B) == 0:
break
B.sort(key=lambda p: (p.cost, len(p.path)))
A.append(B[0].path)
B.pop()
return A
paths = YenKSP(graph, 'A', 'F', 4)
print(paths)
import copy
import heapq
#from dotmap import Map
from itertools import count
class Map(dict):
def __getattr__(self, k):
return self[k]
def __setattr__(self, k, v):
self[k] = v
graph = {
'A': ['B', 'E'],
'B': ['C', 'D'],
'C': ['G'],
'D': ['C', 'F'],
'E': ['D'],
'F': [],
'G': ['F']
}
class PriorityQueue:
def __init__(self):
self.elements = []
self._counter = count()
def empty(self):
return len(self.elements) == 0
def put(self, item, priority):
heapq.heappush(self.elements, (priority, item,))
def get(self):
return heapq.heappop(self.elements)[1]
class AStar:
def __init__(self, graph, start, goals=[]):
self.graph = graph
self.start = start
self.frontier = PriorityQueue()
self.frontier.put(start, 0)
self.previous = {}
self.previous[start] = None
self.costs = {}
self.costs[start] = 0
self.final_path = None
self.goals = goals
self.goal = None
def search(self):
graph = self.graph
frontier = self.frontier
goals = self.goals
costs = self.costs
while not frontier.empty():
state = frontier.get()
if state in goals:
cost = self.costs[state]
self.goal = state
self.final_path = self.trace_path()
return Map({'path': self.final_path, 'cost': cost})
for next_state in graph[state]:
new_cost = costs[state] + 1
if next_state not in costs or new_cost < costs[next_state]:
costs[next_state] = new_cost
priority = new_cost
frontier.put(next_state, priority)
self.previous[next_state] = state
# No path found
return Map({'path': [], 'cost': float('inf')})
def trace_path(self):
current = self.goal
path = []
while current != self.start:
path.append(current)
current = self.previous[current]
path.append(self.start)
path.reverse()
return path
def YenKSP(graph, source, sink, k_paths):
A = [AStar(graph, source, sink).search().path]
B = []
for _ in range(1, k_paths):
for i in range(len(A[-1]) - 1):
graph_clone = copy.deepcopy(graph)
spur_node = A[-1][i]
root_path = A[-1][:i+1]
for path in A:
if len(path) > i and root_path == path[:i+1]:
if path[i+1] in graph_clone[path[i]]:
graph_clone[path[i]].remove(path[i+1])
result = AStar(graph_clone, spur_node, sink).search()
spur_path = result.path
total_path = root_path[:-1] + spur_path
spur_cost = AStar(graph_clone, source, spur_node).search().cost
B.append(Map({'path': total_path, 'cost': result.cost + spur_cost}))
if len(B) == 0:
break
B.sort(key=lambda p: (p.cost, len(p.path)))
best_b = B.pop(0)
if best_b.cost != float('inf'):
A.append(best_b.path)
return A
paths = YenKSP(graph, 'A', 'F', 4)
print(paths)
Produces:
[['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], ['A', 'B', 'C', 'G', 'F'], ['A', 'B', 'D', 'C', 'G', 'F']]
The main issue was that when there was no path found, your default returned a path with 0 cost. So when sorted by path cost, these paths were appearing as the best choice in B and being added to A. I changed the default path cost to float('inf'). Doing so revealed an error that could occur when you tried to remove the same edge twice from graph_clone (inside for path in A: ...), so I added an if check to conditionally remove the edge. The two last things the diff indicate that I did were (a) imitate your dotmap.Map class (you can remove this and uncomment the import), and (b) only add a path to the resultset A if the cost is finite.
I want to find a list of unique items and sublists in a list in python 2.7
Input : [['P', 'Q'], [['not', 'P'], 'R'], [['not', 'R'], ['not', 'P']], [['not', 'Q'], 'S', ['not', 'T']], 'T']
Output: ['P','Q',['not','P'],'R',['not','R'],['not','Q'],'S',['not','T'],'T']
Can anyone suggest how to do it recursively?
My code :
def rem_dup(lis):
y, s = [], set()
for t in lis:
w = tuple(sorted(t)) if isinstance(t, list) else t
if not w in s:
y.append(t)
s.add(w)
return y
def removeDuplicates(prop):
if isinstance(prop, str):
return prop
else:
out = [rem_dup(i) if isinstance(i, list) else i for i in rem_dup(prop)]
return out
I call removeDuplicates from main method and pass it input. I get below exception:
if not w in s:
TypeError: unhashable type: 'list'
Recursive solution:
def unique(lst):
s = set()
for el in lst:
if isinstance(el, str):
s.add(el)
elif el[0] == 'not'
s.add(tuple(*el))
else:
s.update(unique(el))
return s
I need a list-like object that will "autogrow" whenever a slot number greater or equal to its length is accessed, filling up all the newly created slots with some pre-specified default value. E.g.:
# hypothetical DefaultList class
x = DefaultList(list('abc'), default='*')
x[6] = 'g'
print x[2], x[4], x[6], x[8] # should print 'c * g *'
Thanks!
PS. I know it is not hard to implement a class like this, but I avoid wheel-reinvention as much as possible, especially if a particularly efficient/well-designed wheel already exists.
PS2. A dict (or a collections.defaultdict) is not an acceptable implementation of the desired data structure. For why, see here: http://groups.google.com/group/comp.lang.python/msg/bcf360dfe8e868d1?hl=en
class DefaultList(list):
def __init__(self,*args,**kwargs):
list.__init__(self,*args)
self.default=kwargs.get('default',None)
def __getitem__(self,key):
# retrieving an item does not expand the list
if isinstance(key,slice):
return [self[elt] for elt in range(key.start,key.stop,key.step)]
else:
try:
return list.__getitem__(self,key)
except IndexError:
return self.default
def __setitem__(self,key,value):
# setting an item may expand the list
try:
list.__setitem__(self,key,value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
x = DefaultList(list('abc'), default='*')
print(x)
# ['a', 'b', 'c']
x[6] = 'g'
print(x)
# ['a', 'b', 'c', '*', '*', '*', 'g']
print x[2], x[4], x[6], x[8] # should print 'c * g *'
# c * g *
print(x[2:9:2])
# ['c', '*', 'g', '*']
I would use a sparse data structure (1xn matrix).
You could always make a function that handles this:
def fillList(item, slot, myList):
length = len(myList)
if slot > length:
augmentation = [item for x in range(slot-length)]
myList.extend(augmentation)
else:
myList[slot] = item
Which while not a data structure, does accomplish what you want.
Using the idea of wheaties's solution and making a prettier interface:
You could inherit from list and overwrite the list 'getitem(index)' method which maps to [index] in your class. It should be something like this:
class GrowingList(list):
def __getitem__(self, index):
length = len(self)
# list is 0 indexed
if index >= length:
tail = [ self.default_value for x in range(index - length + 1)]
self.extend(tail)
return super(self.__class__, self).__getitem__(index)
This same code can be used if you don't extend the list, but just return some default value on invalid index
This preserves the whole list interface.
(This isn't a new answer; just a comment on unutbu's. It should really be possible to post stuff like this in comments; it isn't, so I have to post it as an answer.)
CombineListClasses and CombineListClasses2 inherit from two classes that both inherit from list. The behavior and doctests are straightforward, but break badly in the original version.
This is all standard practice in Python's data model; you almost never should be calling a base class method directly rather than via super.
class DefaultList(list):
"""
>>> x = DefaultList('abc', default='*')
>>> x
['a', 'b', 'c']
>>> x[6] = 'g'
>>> x
['a', 'b', 'c', '*', '*', '*', 'g']
>>> x[2], x[4], x[6], x[8] # should print 'c * g *'
('c', '*', 'g', '*')
>>> x[2:9:2]
['c', '*', 'g', '*']
>>> x = DefaultList()
>>> x[1] = 'a'
>>> x
[None, 'a']
>>> x = DefaultList(sequence=[1,2,3], default=5)
>>> x
[1, 2, 3]
>>> x[10]
5
"""
def __init__(self, *args, **kwargs):
if 'default' in kwargs:
self.default = kwargs['default']
del kwargs['default']
else:
self.default = None
super(DefaultList, self).__init__(*args, **kwargs)
def __getitem__(self, key):
# retrieving an item does not expand the list
if isinstance(key, slice):
return [self[elt] for elt in range(key.start, key.stop, key.step)]
else:
try:
return super(DefaultList, self).__getitem__(key)
except IndexError:
return self.default
def __setitem__(self, key, value):
# setting an item may expand the list
try:
super(DefaultList, self).__setitem__(key, value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
# Another class that derives from list:
class AddMethodToList(list):
def __init__(self, *args, **kwargs):
self.value = kwargs['value']
del kwargs['value']
super(AddMethodToList, self).__init__(*args, **kwargs)
def new_method(self):
return self.value
# Derive from both classes.
class CombineListClasses(AddMethodToList, DefaultList):
"""
>>> a = CombineListClasses(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
# Derive from both classes in reverse, reversing the call chain order.
class CombineListClasses2(DefaultList, AddMethodToList):
"""
>>> a = CombineListClasses2(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
if __name__ == '__main__':
import doctest
print doctest.testmod()
Note that in Python 3, this is supported by the language directly:
class DefaultList(list):
def __init__(self, *args, default=None, **kwargs):
self.default = default
super(self).__init__(*args, **kwargs)
but that's not supported in Python 2. http://www.python.org/dev/peps/pep-3102