Why is this function considered an implementation of a queue? - python-3.x

these examples come from Learning Python by Mark Lutz. The first function is a recursive function used to traverse a list with arbitrary nesting in order to calculate the sum of elements:
def sumtree_rec(L):
tot = 0
for x in L:
if not isinstance(x, list):
tot += x
else:
tot += sumtree(x)
return tot
The second function achieves the same thing, but without recursion:
def sumtree_notrec(L):
tot = 0
items = list(L)
while items:
front = items.pop(0)
if not isinstance(front, list):
tot += front
else:
items.extend(front)
return tot
I believe I understand how both of these functions work. I traced out how L changes in sumtree_notrec with each iteration over the code body, and it matches up with the output from the book. I also think I understand why the recursion is considered a stack, since every level pushes a call frame onto the runtime stack, and is popped off whenever the call is complete.
What I don't understand is why the recursive function is referred to as a FIFO queue? I looked it up and I feel like I understand what the data structures represent, I just don't see how they apply to this function. I also found this resource which explained a bit about the call stack: https://www.cs.ucsb.edu/~pconrad/cs8/topics.beta/theStack/02/
For example, if I trace through L in the non recursive function (not actual code, just a representation):
L --> [1,[2,[3,4],5],6,[7,8]]
L --> (1) is popped [[2,[3,4],5],6,[7,8]]
L --> [2,[3,4],5] is not popped
L --> [6,[7,8],2,[3,4],5]
etc...
Why is this called a queue? What object is 'first in' and then 'first out'?

The recursive version is a depth-first search. The non-recursive version is a breadth-first search. In the non-recursive version, the items list is treated as a queue. Whenever a list is popped from items, the individual elements in that list are added to the end of items.
That's the simple definition of a queue: elements are added to the back and removed from the front.

Related

Finding item at index i of an iterable (syntactic sugar)?

I know that maps, range, filters etc. in python3 return iterables, and only calculate value when required. Suppose that there is a map M. I want to print the i^th element of M.
One way would be to iterate till i^th value, and print it:
for _ in range(i):
next(M)
print(next(M))
The above takes O(i) time, where I have to find the i^th value.
Another way is to convert to a list, and print the i^th value:
print(list(M)[i])
This however, takes O(n) time and O(n) space (where n is the size of the list from which the map M is created). However, this suits the so-called "Pythonic way of writing one-liners."
I was wondering if there is a syntactic sugar to minimise writing in the first way? (i.e., if there is a way which takes O(i) time, no extra space, and is more suited to the "Pythonic way of writing".)
You can use islice:
from itertools import islice
i = 3
print(next(islice(iterable), i, i + 1))
This outputs '3'.
It actually doesn't matter what you use as the stop argument, as long as you call next once.
Thanks to #DeepSpace for the reference to the official docs, I found the following:
from more_itertools import nth
print(nth(M, i))
It prints the element at i^th index of the iterable.

Why is my merge sort algorithm not working?

I am implementing the merge sort algorithm in Python. Previously, I have implemented the same algorithm in C, it works fine there, but when I implement in Python, it outputs an unsorted array.
I've already rechecked the algorithm and code, but to my knowledge the code seems to be correct.
I think the issue is related to the scope of variables in Python, but I don't have any clue for how to solve it.
from random import shuffle
# Function to merge the arrays
def merge(a,beg,mid,end):
i = beg
j = mid+1
temp = []
while(i<=mid and j<=end):
if(a[i]<a[j]):
temp.append(a[i])
i += 1
else:
temp.append(a[j])
j += 1
if(i>mid):
while(j<=end):
temp.append(a[j])
j += 1
elif(j>end):
while(i<=mid):
temp.append(a[i])
i += 1
return temp
# Function to divide the arrays recursively
def merge_sort(a,beg,end):
if(beg<end):
mid = int((beg+end)/2)
merge_sort(a,beg,mid)
merge_sort(a,mid+1,end)
a = merge(a,beg,mid,end)
return a
a = [i for i in range(10)]
shuffle(a)
n = len(a)
a = merge_sort(a, 0, n-1)
print(a)
To make it work you need to change merge_sort declaration slightly:
def merge_sort(a,beg,end):
if(beg<end):
mid = int((beg+end)/2)
merge_sort(a,beg,mid)
merge_sort(a,mid+1,end)
a[beg:end+1] = merge(a,beg,mid,end) # < this line changed
return a
Why:
temp is constructed to be no longer than end-beg+1, but a is the initial full array, if you managed to replace all of it, it'd get borked quick. Therefore we take a "slice" of a and replace values in that slice.
Why not:
Your a luckily was not getting replaced, because of Python's inner workings, that is a bit tricky to explain but I'll try.
Every variable in Python is a reference. a is a reference to a list of variables a[i], which are in turn references to a constantant in memory.
When you pass a to a function it makes a new local variable a that points to the same list of variables. That means when you reassign it as a=*** it only changes where a points. You can only pass changes outside either via "slices" or via return statement
Why "slices" work:
Slices are tricky. As I said a points to an array of other variables (basically a[i]), that in turn are references to a constant data in memory, and when you reassign a slice it goes trough the slice element by element and changes where those individual variables are pointing, but as a inside and outside are still pointing to same old elements the changes go through.
Hope it makes sense.
You don't use the results of the recursive merges, so you essentially report the result of the merge of the two unsorted halves.

Is this recursive?

Second attempt here, I just wanted to know if this is considered a recursive function.
The purpose of the function is to take a string and
if the the first element is equal to the last element
then append the last element to a list and return nothing,
else call istelf and pass the same string from index [1]
finally append the first element to the list
I know that error checking needs to be done on the if statement. However I am only doing this to try and get my head around recursion...Struggling to be honest.
Also I would never write a program like this if it where anything but trivial I just wanted to check if my understanding is correct so far.
def parse(theList):
theList.reverse()
parsedString = ''.join(theList)
return parsedString
def recursiveMessage(theString):
lastElement = theString[len(theString) - 1]
if theString[0] == lastElement:
buildString.append(theString[0])
return None
else:
recursiveMessage(theString[1::])
buildString.append(theString[0])
toPrint = "Hello Everyone!"
buildString = []
recursiveMessage(toPrint)
print(parse(buildString))
Thanks again.
Is this recursive?
If at any point in a function's execution it calls itself, then it is consider recursive. This happens in your example, so recursiveMessage is indeed recursive.
so which is quicker recursion or iteration?
Recursion is usually much slower and consumes more space due to a new stack frame having to be created on the call stack each recursive call. If you know your recursive function will need to be run many times, iteration is the best route.
As an interesting side note, many compilers actually optimize a recursive function by rolling it out into a loop anyways.

Python For Loop - Switching from PHP

I am new to python and usually use PHP. Can someone please clear me structure of For loop for python.
numbers = int(x) for x in numbers
In PHP everything realted to For loop used to inside the body. I can't understand why methods are before for loop in python.
First of all the statement is missing brackets:
numbers = [int(x) for x in numbers]
This is called a list comprehension and it is the equivalent of:
numbers = []
for x in numbers:
numbers.append(int(x))
Note that you can also use comprehensions as generator expressions, in which case the [] become ():
numbers = (int(x) for x in numbers)
which is the equivalent of:
def numbers(N):
for x in N:
yield int(x)
This means that the for loop will only execute one yield at the time as it is being processed. In other words, while the first example builds a list in memory, a generator returns one element at a time when executed. This is great to process large lists where you can generate one element a time without getting everything into memory (e.g. processing a file line-by-line).
So as you can see comprehensions and generator expressions are a great way to reduce the amount of code required to process lists, tuples and any other iterable.

Two parameters in a predicate function

Is there a way that I can write a predicate function that will compare two strings and see which one is greater? Right now I have
def helper1(x, y):
return x > y
However, I'm trying to use the function in this way,
new_tuple = divide((helper1(some_value, l[0]),l[1:])
Please note that the above function call is probably wrong because my helper1 is incomplete. But the gist is I'm trying to compare two items to see if one's greater than the other, and the items are l[1:] to l[0]
Divide is a function that, given a predicate and a list, divides that list into a tuple that has two lists, based on what the predicate comes out as. Divide is very long, so I don't think I should post it on here.
So given that a predicate should only take one parameter, how should I write it so that it will take one parameter?
You should write a closure.
def helper(x):
def cmp(y):
return x > y
return cmp
...
new_tuple = divide(helper1(l[0]), l[1:])
...

Resources