How to peek into collections.deque() next item in Python-3? - python-3.x

[EDIT3]: Trying to shortly and simply explane why do I need to implement it this way made me re-thing the whole procedure and the new solution came to my mind, simpler and shorter (:D) It doesn't need the Nr_Heap class at all. So, in a way, case dismissed.
[EDIT1]: I've edited the question, maybe it will be more understandable now.
I'm implementing an algorithm that is based on some computational geometry procedures. The input is a polyline build of points:
class Point:
def __init__(self, nr=0, x=0, y=0):
self._x = x
self._y = y
self._nr = nr
self._hierarchy = None
self._is_left = 0 #0 - pt is on line
#1 - pt is on the left side of the line
#-1 - pt is on the right side of the line
self._father = None
self._mother = None
self._nr_max = 0
[.........]
The process starts from preparing Regions consisting of consecutive polyline points that are on the same (left or right) side of a line connecting Start and End polyline points (aka SE Line).
Preparing the Regions:
For each point of processed polyline count if its on the left or right side of SE Line,
Along the way prepare an intersection_list of points where a polyline's segment intersects SE Line (i.e. is_left property changes from left to right, or the other way around), marking start and end points of such segment,
Catch points on the left of SE Line and put them on an auxiliary Nr_Heap queue,
Divide points on this Nr_Heap into Regions (that is make a new region when points on the Nr_Heap are no longer consecutive),
Repeat 3. and 4. for the points on the right side of SE Line.
Nr_Heap is planned to be a collections.deque() data structure, like below. I'm omitting unrelevant functions in the code.
class Nr_Heap:
"""
Deque of points, auxiliary to defying a Region_List
"""
def __init__(self):
self._top = None
self._tmp_bottom = None
#property
def top(self):
return self._top
#top.setter
def top(self, queue):
self._top = queue #queue: collections.deque()
def pop(self):
if self.top:
try:
self.top.popleft()
except IndexError:
#raise IndexError(f"The queue is empty, nothing to pop.")
return None
else:
raise ValueError(f"Nr_Heap queue not initiated, no queue to push point into.")
return self.top
I need to implement a Nr_Heap clean function, that pops items, starting from the top, but only while they meet two criteria (see 4. above). Those are:
(1) next item to the current one exists and
(2) the absolute value of difference of those two (current and next) items ._nr (see Points class) values equals 1.
Those popped points are added to a Region structure. When no such items exists the popping stops, what means that after such cleaning there can still be more points in the Nr_Heap, waiting for new Region to be started.
I tried like this:
def clean(self):
if self.top:
p = self.top
it = iter(self.top)
try:
n = next(it)
n = next(it)
except StopIteration as e:
n = None
while n and abs(p[0].nr - n.nr) == 1:
p = self.pop()
try:
n = next(it)
except StopIteration as e:
n = None
return self.top
I get a RuntimeError: deque mutated during iteration (not a surprise, if I'm popping an item...), but I have no other idea how to do it.
So my question is: using collections.deque() - how to peek into next item and check its properties to have conditions for a "while" loop? Is it possible to peek into next item of a deque() at all? Or maybe into a list, or some other python's data structure that I could use instead? I could implement my own heap structure, of course, but I wanted to try with the Python's build ones.
[EDIT2]: I got enlighted (sort of ;)) and now it is doing what it is supposed to do (see the code below). But if someone could do it better or answer my question above I'd be most grateful.
def clean(self):
while self.top:
it = iter(self.top)
try:
n = next(it)
n = next(it)
except StopIteration as e:
n = None
if n and abs(self.top[0].nr - n.nr) == 1:
self.pop()
else:
break
return self.top

Related

how the turtle "screen.onkey" module works? need to understand this piece of code

i'm studying python on "thinking python" book and i'm trying to do the exercise. at the moment i'm studying this code which is part of the chapter 4 exercises. i ran this code (it needs the "letters" module, which i have):
"""This module contains a code example related to
Think Python, 2nd Edition
by Allen Downey
http://thinkpython2.com
Copyright 2015 Allen Downey
License: http://creativecommons.org/licenses/by/4.0/
"""
from __future__ import print_function, division
import string
import turtle
"""
To use this typewriter, you have to provide a module named letters.py
that contains functions with names like draw_a, draw_b, etc.
"""
# check if the reader has provided letters.py
try:
import letters
except ImportError as e:
message = e.args[0]
if message.startswith('No module'):
raise ImportError(message +
'\nYou have to provide a module named letters.py')
def teleport(t, x, y):
"""Moves the turtle without drawing a line.
Postcondition: pen is down
t: Turtle
x: coordinate
y: coordinate
"""
t.pu()
t.goto(x, y)
t.pd()
def keypress(char):
"""Handles the event when a user presses a key.
Checks if there is a function with the right name; otherwise
it prints an error message.
char: string, letter to draw
"""
# if we're still drawing the previous letter, bail out
if bob.busy:
return
else:
bob.busy = True
# figure out which function to call, and call it
try:
name = 'draw_' + char
func = getattr(letters, name)
except AttributeError:
print("I don't know how to draw an", char)
bob.busy = False
return
func(bob, size)
letters.skip(bob, size/2)
bob.busy = False
def carriage_return():
"""Moves to the beginning of the next line.
"""
teleport(bob, -180, bob.ycor() - size*3)
bob.busy = False
def presser(char):
"""Returns a function object that executes keypress.
char: character to draw when the function is executed
returns: function with no arguments
"""
def func():
keypress(char)
return func
# create and position the turtle
size = 20
bob = turtle.Turtle()
bob.busy = False
teleport(bob, -180, 150)
# tell world to call keypress when the user presses a key
screen = bob.getscreen()
for char in string.ascii_lowercase:
screen.onkey(presser(char), char)
screen.onkey(carriage_return, 'Return')
screen.listen()
turtle.mainloop()
even if i haven't studied the cycles etc yet i understand the code, but what i don't understand it's this part:
for char in string.ascii_lowercase:
screen.onkey(presser(char), char)
screen.onkey(carriage_return, 'Return')
the onkey function should get a function without argument, but in this case it takes the presser(char) function and it works. other than that i don't understand if the onkey can bind infinite function to it. that "for" cycle basically attach every character of the alphabet to the onkey, so does onkey keep track of all function that i pass as argument? i hope i explained myself

Returning elements to stack in original order without using temporary stack

I have a stack S containing n elements and a queue Q that is initially empty. I have to implement an algorithm that uses Q to scan S to see if it contains a certain element x, with the additional constraint that my algorithm must return the elements back to S in their original order. The compulsion is that I may only use S, Q, and a constant number of other variables.
I have implemented this algorithm having used a temporary stack to hold elements and then return them to the original stack in their original order but how do I accomplish this task without using a temporary stack?
if __name__ == '__main__':
def scan(S, Q, x):
for i in range(10):
S.push(i)
S1 = ArrayStack()
flag = False
for i in range(len(S)):
Q.enqueue(S.pop())
if Q.first() == x:
flag = True
print("Your desired element has been found:", Q.first())
S1.push(Q.dequeue())
break
else:
S1.push(Q.dequeue())
if flag == False:
print("Sadly, your desired element could not be found.")
for i in range(len(S1)):
S.push(S1.pop())
scan(ArrayStack(), LinkedQueue(), 9)
The trick is to first put the elements from the queue back on the stack -- which will put them in reversed order -- but then repeat the process again for the same number of values: pop them from the stack into the queue and then flush the queue back to the stack again. Now they will be back in their original order.
Not sure whether the function's signature has been given to you like that, but I would not pass Q as argument, since it only serves the function's algorithm, not the caller. On the other hand, I would not initialise the stack inside the function, but let the caller deal with populating the stack. That way the caller controls which stack data to call the function with.
So this is what you could do:
def scan(S, x):
Q = LinkedQueue() # Create the queue here
found = False
for i in range(len(S)):
Q.enqueue(S.pop())
found = Q.first() == x
if found:
break
i += 1 # The number of values that are currently in the queue
# Flush the queue on the stack
for j in range(i):
S.push(Q.dequeue())
# They are reversed on the stack, so remove them again
for j in range(i):
Q.enqueue(S.pop())
# and finally flush the queue again
for j in range(i):
S.push(Q.dequeue())
return found
Call like this:
S = ArrayStack()
# Initialise the stack here
for i in range(10):
S.push(i)
# Get the boolean result from the function call
found = scan(S, 12)
# Use it to display a message
if found:
print("Your desired element has been found")
else:
print("Sadly, your desired element could not be found.")

Generate all valid binary search trees given a list of values

Hello I am trying to solve the following question on leetcode, [https://leetcode.com/problems/unique-binary-search-trees-ii/].
I know I have access to the solution but I tried solving the problem my way and I am stuck and I would like to know if it is solvable the way I am doing it.
Here is the code:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def generateTrees(myrange, n, res = None):
if res == None:
res = []
if myrange == []:
res.append(None)
return
for root in myrange:
res.append(root)
generateTrees([i for i in range(root) if i in set(myrange)], n, res) #leftchild
generateTrees([i for i in range(root+1, n) if i in set(myrange)], n, res) #rightchild
return res
Initially myrange is just the list containing the node values, and n is the length of myrange.
The way I am doing it is a sort of DFS where I loop over the nodes making each one of them the root once and then I do the same for the left and right subtrees to get all combinations. But the problem I am facing is I can't figure out how to manage res to remove elements from it as my recursion backtracks (and make it so res only contains valid bst's and then put those in some other list that will be my actual result).
I would like some pointers or even just comments on if you think my approach is valid or bad ..etc.
Issues:
As you mention, your code only creates one list to which it keeps appending.
Even if you would fix that, the lists would never come out in the BFS kind of order, which is what the question's example seems to suggest.
For a chosen root, you need to list all combinations of its possible left subtrees with its possible right subtrees -- a Cartesian product if you wish. This logic is missing in your code.
I would:
not pass res as argument to the recursive function. Just return it, and let the caller deal with it.
not use ranges, as that only seems to complicate things. The if i in set(myrange) seems like an inefficient way to get the overlap between two ranges. I would instead pass the two extremes of the range as separate arguments.
use the TreeNode class to actually create the trees, and deal with generating the required output format later.
For generating the output format you need a BFS walk through the tree, and this could be implemented as a method on TreeNode.
Here is what I think would work:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def breadth_first(self):
lst = []
todo = [self]
while any(todo):
node = todo.pop(0)
lst.append(node.val if node else None)
if node:
todo.append(node.left)
todo.append(node.right)
return lst
def generateTrees(n):
def recur(start, end): # end is not included
if start >= end:
return [None]
trees = []
for root in range(start, end):
lefts = recur(start, root)
rights = recur(root+1, end)
# Cartesian product:
for left in lefts:
for right in rights:
# Start with a new tree, and append to result
tree = TreeNode(root)
tree.left = left
tree.right = right
trees.append(tree)
return trees
return recur(1, n+1)
# Create the trees as a list of TreeNode instances:
trees = generateTrees(3)
# Convert to a list of lists
print([tree.breadth_first() for tree in trees])

Reverse a singly linked list

Reverse a singly linked list.
Example:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
Here's what I saw online:
class Solution:
def reverseList(self, head):
def reverse(prev, cur):
if cur:
reverse(cur, cur.next)
cur.next = prev
else:
nonlocal head
head = prev
reverse(None, head)
return head
But I didn't get the process how it works after the if cur.
For example the test case is [1,2,3,4,5].
input None, 1 into the reverse function
cur exist, run reverse function with 1, 2
...
cur exist, run reverse function with 4, 5
cur does not exist, (then what???)
BTW why does listNode even exist? It is not as easy as others like list array or dictionary. When should i use it?
There's no efficient way to do this. The only way without interacting with the list beforehand, is an algorithm of a complexity of O(n * n), where you repeatedly go through the linked list, find last element and remove it, while adding it to a new linked list, until the first list is completely empty. If you wanted to preserve that list, you'd have to recreate that list during the reversing or simply copy it beforehand, not fun at all. This is what example you posted does.
def reverse(prev, cur):
if cur: # this should actually be if cur is not None:
reverse(cur, cur.next) # call same method on next node
cur.next = prev # when we are back from recursion hell, set previous one as next one, because we are reversing
else: # we finally reached the end
nonlocal head # excuse me, this is in a class but you wrap in multiple functions and use nonlocal??? Where did you even dig out this thing from?
head = prev # head now becomes the previous node, because this node is None, or, end of the list
If you just want to learn about them, then this algorithm is perfectly fine, in any other case, you should at least upgrade to doubly linked list, you'll use insignificant amount of memory to speed up reversal a lot.
If you create your own doubly linked list, reversing could be as fast as swapping end and start "pointers", if you write it in a way where every node has two references, left and right in a list, such as neighbours = [left, right], your linked list could store a value called advance direction, which would be either 0 or 1, meaning that you could reverse entire list from start to end while going to left to right to end to start while going right to left by merely swapping start with end and changing "advance value" from 1 to 0. The advance value would be wrapped in a method like next(), which would look something like this:
def next(self):
if self.current is None:
return None # could be return self.current or even just "return", this is Python
self.current = self.current.neighbours[self.advance_value]
return self.current
When advance_value is 0, it goes left, when advance_value is 1, it goes right through the list, all elements stay in their place, but to whoever is accessing it, it looks like it's reversed.

Creating a (very simple) computer opponent for connect4

I am currently having problem writing a function that should return a suggested move i.e. a simple computer opponent for a game of connect 4.
The following is some functions that I have written (and work correctly for the situations I have tested) but are used in the computeropponent function.
import random
#board: a list containing 6 lists with 7 elements each (a connect4 board)
#0 represents an empty space, and 1,2 represent player1 and player2
#turn: whose turn it is (i.e. player1 or player2)
#move: column they want to make a move in (i.e 0-6, but will be 1-7 to the player)
def validmoves(board):
"""This function iterates through the board to find all the valid moves
(i.e. it excludes the columns that are filled) and returns the list of
columns with valid moves"""
vm = []
for j in range(7):
i = 5
while vm.count(j) == 0 and i>=0:
if board[i][j] == 0:
vm.append(j)
i -= 1
return vm
def makemove(board,move,turn):
"""This function places turn's player piece in the move'th column and
returns the board"""
for i in reversed(board):
if i[move] == 0:
i[move] = turn
return board
def winner(board,turn):
"""This function checks if the player who just played a turn has won"""
#horizontals
for i in board:
for j in range(4):
if i[j]==turn and i[j+1]==turn and i[j+2]==turn and i[j+3]==turn:
return True
#verticals
for j in range(7):
for i in range(3):
if board[i][j]==turn and board[i+1][j]==turn and board[i+2][j]==turn and board[i+3][j]==turn:
return True
#positive diagonals
for j in range(4):
for i in range(3):
if board[i][j]==turn and board[i+1][j+1]==turn and board[i+2][j+2]==turn and board[i+3][j+3]==turn:
return True
#negative diagonals
for j in range(4):
for i in range(5,2,-1):
if board[i][j]==turn and board[i-1][j+1]==turn and board[i-2][j+2]==turn and board[i-3][j+3]==turn:
return True
return False
The following is the code I have a problem with
def computeropponent(board,turn):
#the following defines the other player in the context of this function
if turn == 1:
notturn = 2
elif turn == 2:
notturn = 1
"""for the following loops, it only computes the first loop and then skips straight to
the last line."""
for move in validmoves(board):
if winner(makemove(board,move,turn),turn)==True:
return move
for move in validmoves(board):
if winner(makemove(board,move,notturn),turn)==True:
return move
return random.choice(validmoves(board))
i.e. if i were to swap the order of the for loops (or set turn to 1 in
computer opponent), it will return the correct answer (which should be 0 in the test below), however in this case it just returns a random element from the list of valid moves.
board = [[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
[0,0,0,0,1,2,0],
[0,0,0,1,2,2,0],
[0,1,1,1,2,1,0]]
print(validmoves(board))
print(computeropponent(board,2))
I apologise in advance for the messy code and length of my post. I have not been programming long and this is my first time posting on stackexchange, and thanks in advance for the help.

Resources