Python - Depth First labyrinth solver - python

I have a homework in python, where i am supposed to make a "robot" get from the start to the finish, and return the path to the goal. I have the robot searching, but i want the list to just show the path from start to finish. Right now the pathList returns all visited squares.. and also, when it comes to the goal it doesn't stop, just continue with the other nodes.
What am i missing?
def find(labyrinth, robotPos, pathList = []):
frontier = adjacent_passages(labyrinth, robotPos)
pathList.append(robotPos)
if len(frontier) == 1:
print("Corner")
return []
for i in range(0, len(frontier)):
if frontier[i] == goal:
pathList.append(frontier[i])
return pathList
for i in range(0, len(frontier)):
if frontier[i] not in pathList:
pathList.append(frontier[i])
if (find(labyrinth, frontier[i], pathList) == []):
pathList.pop()
return pathList

I don't know if this is the answer to your problem, but the first thing I notice is you shouldn't be using a list as a default function parameter (pathList = []).
See "Least Astonishment" and the Mutable Default Argument

You're appending robotPos to pathList without removing it even if the search fails, that's why the list contains all visited positions. I'd suggest to avoid list mutations (append/pop) altogether and instead pass a new value as an argument to the next iteration:
if (find(labyrinth, frontier[i], pathList + [frontier[i]]) == [])...
Other possible simplifications are:
drop robotPos. Let the current position be the last item of the path argument
use one loop instead of two
in loops, use for x in stuff instead of for i in range(0, len(stuff))
Something like
def find(path):
if path[-1] == goal:
return path
for new_position in adjacent_positions(path[-1]):
if new_position not in path:
found_path = find(path + [new_position])
if found_path:
return found_path
return None

Related

Why the path variable cannot be returned?

I am using DFS to get all routes between two nodes.
My python code is as follows:
graph = {0: [1, 2, 3],
1: [3],
2: [0, 1],
3: []}
def DFS(start, stop, path=[], visited=[]):
global count
global result
# add the visited node to path
path.append(start)
# mark this node visited to avoid infinite loop
visited.append(start)
# found
if start == stop:
print(path)
else:
# if not found
values = graph.get(start)
for next_ in values:
# not visited node
if not next_ in visited:
DFS(next_, stop, path, visited)
# remove the node from path and unmarked it
path.remove(start)
visited.remove(start)
The problem is that if I print path in if start == stop, all 3 routes can be printed correctly.
>>> DFS(2, 3)
[2, 0, 1, 3]
[2, 0, 3]
[2, 1, 3]
But if I change to return path in if start == stop, it would return nothing.
def DFS(start, stop, path=[], visited=[]):
global count
global result
# add the visited node to path
path.append(start)
# mark this node visited to avoid infinite loop
visited.append(start)
# found
if start == stop:
return path
else:
# if not found
values = graph.get(start)
for next_ in values:
# not visited node
if not next_ in visited:
DFS(next_, stop, path, visited)
# remove the node from path and unmarked it
path.remove(start)
visited.remove(start)
>>> result = DFS(2, 3)
>>> result
But if I change to return path in if start == stop, it would return nothing.
Right; because you got to this level of recursion from the previous one, which recursively called DFS(next_, stop, path, visited)... and ignored the result.
It is the same as if you called functions normally:
def inner():
return "hello"
def outer():
inner() # oops, it is not returned.
print(outer()) # None
In general you want to return the results from your recursive calls; but your case is a little special because you need to accumulate the results from multiple recursive calls (for next_ in values:). You could build a list and return it, but this is a bit tricky:
if start == stop:
result = [path] # for uniformity, we need a list of paths in this case too.
# Also, we can't `return` here, because we'll miss the cleanup at the end.
else:
result = []
values = graph.get(start)
for next_ in values:
# BTW, Python treats `not in` as a single operator that does
# what we want here. It's preferred because it's easier to read.
if next_ not in visited:
# add results from the recursive call to our result.
result.extend(DFS(next_, stop, path, visited))
# it is `.extend` and not `.append` here because otherwise we will
# build a tree of nested lists - do you understand why?
# Either way, we want to do our cleanup, and return the collected result.
path.remove(start)
visited.remove(start)
return result # important!
Tricky, right?
My preferred solution for these situations, therefore, is to write a recursive generator, and collect the results outside the recursion:
# Inside the function, we do:
if start == stop:
yield path
else:
values = graph.get(start)
for next_ in values:
if next_ not in visited:
yield from DFS(next_, stop, path, visited))
path.remove(start)
visited.remove(start)
# Then when we call the function, collect the results:
paths = list(DFS(2, 3))
# Or iterate over them directly:
for path in DFS(2, 3):
print("For example, you could take this route:", path)
(Also, the comment you received was good advice. Recursion is a lot easier to understand when you don't try to mutate the arguments and clean up afterwards. Instead, always pass those arguments, and when you make the recursive call, pass a modified version. When the recursion returns, cleanup is automatic, because you just go back to using the old object in the old stack frame.
The problem with your code is that
result=DFS(3,2)
will only return a valid result if start=stop which is not the case as 3!=2.
To get the desired output you have to change the line
DFS(next_,stop,path,visited)
to
return DFS(next_,stop,path,visited)
Now whenever start gets equal to stop the path will be returned and this value will be propogated upwards

Recursively Generating a List of n choose k combinations in Python - BUT return a list

I'm attempting to generate all n choose k combinations of a list (not checking for uniqueness) recursively by following the strategy of either include or not include an element for each recursive call. I can definitely print out the combinations but I for the life of me cannot figure out how to return the correct list in Python. Here are some attempts below:
class getCombinationsClass:
def __init__(self,array,k):
#initialize empty array
self.new_array = []
for i in xrange(k):
self.new_array.append(0)
self.final = []
self.combinationUtil(array,0,self.new_array,0,k)
def combinationUtil(self,array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
self.final.append(current_combo)
return
if array_index >= len(array):
return
current_combo[current_combo_index] = array[array_index]
#if current item included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index+1,k)
#if current item not included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index,k)
In the above example I tried to append the result to an external list which didn't seem to work. I also tried implementing this by recursively constructing a list which is finally returned:
def getCombinations(array,k):
#initialize empty array
new_array = []
for i in xrange(k):
new_array.append(0)
return getCombinationsUtil(array,0,new_array,0,k)
def getCombinationsUtil(array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
return [current_combo]
if array_index >= len(array):
return []
current_combo[current_combo_index] = array[array_index]
#if current item included & not included
return getCombinationsUtil(array,array_index+1,current_combo,current_combo_index+1,k) + getCombinationsUtil(array,array_index+1,current_combo,current_combo_index,k)
When I tested this out for the list [1,2,3] and k = 2, for both implementations, I kept getting back the result [[3,3],[3,3],[3,3]]. However, if I actually print out the 'current_combo' variable within the inner (current_combo_index == k) if statement, the correct combinations print out. What gives? I am misunderstanding something to do with variable scope or Python lists?
The second method goes wrong because the line
return [current_combo]
returns a reference to current_combo. At the end of the program, all the combinations returned are references to the same current_combo.
You can fix this by making a copy of the current_combo by changing the line to:
return [current_combo[:]]
The first method fails for the same reason, you need to change:
self.final.append(current_combo)
to
self.final.append(current_combo[:])
Check this out: itertools.combinations. You can take a look at the implementation as well.

recursive sorting in python

I am trying to run a sorting function recursively in python. I have an empty list that starts everything but everytime I try to print the list I get an empty list. here is my code. Any help would be greatly appreciated
def parse(list):
newParse = []
if len(list) == 0:
return newParse
else:
x = min(list)
list.remove(x)
newParse.append(x)
return sort(list)
The value of newParse is not preserved between invocations of the function; you're setting it equal to [] (well, you're creating a new variable with the value []).
Since the only time you return is
newParse = []
if len(list) == 0:
return newParse`
you will always be returning [] because that is the value of newParse at that time.
Because you are doing this recursively, you are calling the function anew, without keeping the function's own state. Take a moment to consider the implications of this on your code.
Instead of initialising newParse = [], add an optional parameter newParse defaulting to a bogus value, and set newParse = [] if you receive that bogus value for newParse. Otherwise, you'll actually be getting the same list every time (i.e. the contents of the list object are being mutated). And newParse through in your tail call.
You also seem to have the problem that your definition and and the supposedly-recursive call refer to different functions.
def sort(list, newParse = None):
if newParse is None:
newParse = []
if len(list) == 0:
return newParse
else:
x = min(list)
list.remove(x)
newParse.append(x)
return sort(list, newParse)
Here is what I think you are trying to do:
def recursive_sort(a_list):
def helper_function(list_to_be_sorted, list_already_sorted):
new = []
if len(list_to_be_sorted) == 0:
return list_already_sorted
else:
x = min(list_to_be_sorted)
list_to_be_sorted.remove(x)
new.append(x)
return helper_function(list_to_be_sorted, list_already_sorted + new)
return helper_function(a_list, [])
You shouldn't name variables list, as that is a builtin.
Also, if you are trying to implement a recursive sort function, you might want to look at quicksort, which is a very common (and fast) recursive sorting algorithm. What you have tried to implement is a recursive version of selection sort, which is much slower.
Also, if you actually need a sorting function, rather than just wanting to implement a recursive one, you should use the list method sort, or the function on an iterable sorted, both of which will be a lot faster than anything you could make in Python.

How do I keep a list of numbers I have already enountered?

I am currently working on a BASIC simulator in Python, as the title suggests. Here is my code for this problem:
def getBASIC():
l = []
x = 1
while x == 1:
i = input()
l.append(i)
if len(i.split()) != 3:
x = 0
return l
def findLine(prog, target):
for l in range(0, len(prog)):
progX = prog[l].split()
if progX[0] == target:
return l
def execute(prog):
location = 0
visited = [False] * len(prog)
while True:
T = prog[location].split()[2]
location = findLine(prog, T)
visited[location] = True
if visited[len(visited)-1] == False:
return "infinite loop"
else:
return "success"
The first function does what it is intended to do -- convert input of BASIC code into a list. The second function, findLine also does what it is intended to do, in that it finds the item which contains the string equal to the input. The last function, however, I cannot get to work. I know what I have to do, and that is to check whether or not a part of it has been visited twice. I cannot figure out how to do this, due to the existence of the while loop. As a result of this, the second half of that function is just placeholder. If you could help me figure out how to solve this, it would be greatly appreciated. Thanks.
You keep a list of places that have been visited (you already do this) and then when you encounter a goto, you check if it does to a line that already have been visited, and if it has been visited, you exit.
One mistake right now is that you make a list that is as long as the program is. That's pretty pointless. Just keep a list of the visited line numbers instead, and check with
if current_line in visited:
Try adding an if statement declaring a line in the visited list to be true when it is encountered in the loop. This is my solution:
def execute(prog):
location = 0
visited=[False]*len(prog)
while True:
if location==len(prog)-1:
return "success"
if visited[location]==True:
return "infinite loop"
if visited[location]==False:
visited[location]=True
line2strings=prog[location].split()
T=line2strings[-1]
location=findLine(prog, T)

How to use properly recursion and side effects in python

In a tree structure, I'm trying to find all leafs of a branch. Here is what I wrote:
def leafs_of_branch(node,heads=[]):
if len(node.children()) == 0:
heads.append(str(node))
else:
for des in node.children():
leafs_of_branch(des)
return heads
leafs_of_branch(node)
I don't know why but it feels wrong for me. It works but I want to know if there is a better way to use recursion without creating the heads parameter.
This
def leafs_of_branch(node,heads=[]):
is always a bad idea. Better would be
def leafs_of_branch(node,heads=None):
heads = heads or []
as otherwise you always use the same list for leafs_of_branch. In your specific case it might be o.k., but sooner or later you will run into problems.
I recommend:
def leafs_of_branch(node):
leafs = []
for des in node.children():
leafs.extend(leafs_of_branch(des))
if len(leafs)==0:
leafs.append(str(node))
return leafs
leafs_of_branch(node)
Instead of doing a if len(node.children()==0, I check for len(leafs) after descending into all (possibly zero) children. Thus I call node.children() only once.
I believe this should work:
def leafs_of_branch(node):
if len(node.children()) == 0:
return [str(node)]
else:
x = []
for des in node.children():
x += leafs_of_branch(des) #x.extend(leafs_of_branch(des)) would work too :-)
return x
It's not very pretty and could probably be condensed a bit more, but I was trying to keep the form of your original code as much as possible to make it obvious what was going on.
Your original version won't actually work if you call it more than once because as you append to the heads list, that list will actually be saved between calls.
As long as recursion goes, you are doing it right IMO; you are missing the heads paramater on the recursive call tho. The reason it's working anyway is for what other people said, default parameters are global and reused between calls.
If you want to avoid recursion altogheter, in this case you can use either a Queue or a Stack and a loop:
def leafs_of_branch(node):
traverse = [node]
leafs = []
while traverse:
node = traverse.pop()
children = node.children()
if children:
traverse.extend(children)
else:
leafs.append(str(node))
return leafs
You may also define recursively an iterator this way.
def leafs_of_branch(node):
if len(node.children()) == 0:
yield str(node)
else:
for des in node.children():
for leaf in leafs_of_branch(des):
yield leaf
leafs = list(leafs_of_branch(node))
First of all, refrain from using mutable objects (lists, dicts etc) as default values, since default values are global and reused between the function calls:
def bad_func(val, dest=[]):
dest.append(val)
print dest
>>> bad_func(1)
[1]
>>> bad_func(2)
[1, 2] # surprise!
So, the consequent calls will make something completely unexpected.
As for the recursion question, I'd re-write it like this:
from itertools import chain
def leafs_of_branch(node):
children = node.children()
if not children: # better than len(children) == 0
return (node, )
all_leafs = (leafs_of_branch(child) for child in children)
return chain(*all_leafs)

Categories