I have a project to create a circular array class, and the language I will be using is python. I am new to classes in python, but after reading through some webpages and chapters in books I think I have an understanding of how they work. However I need help, so I figured I would come to the wonderful teachers here at SO :)
Our class must be able to implement several operations; insert at front, insert at back, insert at index, remove from front, remove from back, remove from index.
I have started coding but am running into some problems, and I am not 100% sure if my syntax is even right.
Here is what I have so far:
class circular:
def __init__(self):
self.store = []
self.capacity = len(self.store)
self.size = 0
self.startIndex = 0
self.endIndex = 0
def decrementIndex(self):
index = index - 1
if index < 0:
self.store = self.store + self.capacity
def incrementIndex(self):
index = index + 1
if index == self.capacity:
index = index - self.capacity
def addToBack(self, value):
self.store[self.endIndex] = value
self.endIndex = incrementIndex(self.endIndex)
self.size += 1
def addToFront(self, value):
if self.size == 0:
addToBack(self, value)
else:
self.startIndex = decrementIndex(self.startIndex)
self.store[self.startIndex] = value
self.size += 1
I stopped there to start testing some of the functions, primarily t he addTofront and addToback. Upon testing them in IDLE using c = circular() and c.addToBack(2) I get an index error...and I'm not sure why. That isn't the only problem, it's just where I have gotten stuck and need help moving forward.
I am posting here because I need help and want to learn, not because I am lazy and haven't tried researching my problem. Thanks already SO!
In __init__ you set
self.store = []
In addToBack you do
self.store[self.endIndex] = value
If this is the first operation on the circular array, and you pass 2 as the value, then that turns into
[][0] = 2
The problem should be obvious -- an empty list has no index of 0, it has no indexes at all.
You need to add the item to the list in a different way.
I'm not going to tell you exactly how as it's part of your homework to figure that out.
Related
Design and implement a web browser that supports the functionality that at any given instance
you can efficiently tell the top 5 visited websites on basis of the number of visits (in any order)
In my implementation, I did not use the Webpage class as I can't think of an efficient way we could update the heap based on visits unless we did heapify again. I don't think I can update the heap "on the go". If I use the Webpage class to track visits instead of the hashtable I would still need to update the hashtable every time I visit a site.
I want to understand how I can optimize this solution in PYTHON. I have seen implementations in C++, but I want to know if I can optimize my current implementation in my language of choice. Any insight would be great.
class Webpage:
def __init__(url):
self.url = url
self.numberOfVisits = 1
class History:
def _init_():
self.sites = {}
def visit(url):
if (url in self.sites):
self.sites[url] += 1
else:
self.sites[url] = 1
def printTop5():
heap = []
heapq.heapify(heap)
for key, value in self.sites:
heap.heappush(heap, (-value, key))
i = 0
while (heap and i < 5):
value, url = heapq.heappop(heap)
print(url)
i += 1
def main():
History h = History();
print("before visits\n")
h.visit("www.google.com")
h.visit("nytimes.com")
h.visit("reddit.com")
h.visit("dev.ibm.com")
h.visit("www.google.com")
print("after visits\n")
h.printTop5()
h.visit("ig.com")
h.visit("ig.com")
h.visit("ig.com")
h.printTop5()
Technically, the best way to implement this in Python is to use the built in collections.Counter datatype. This is written in highly optimized code and will probably yield the best performance possible with Python. You can read more about it in the documentation [here] (https://docs.python.org/3/library/collections.html#collections.Counter).
So for example:
from collections import Counter
history = Counter()
#I made them one by one to signify individal browser requests
history["google.com"] += 1
history["yahoo.com"] += 1
history["spotify.com"] += 1
history["yahoo.com"] += 1
history["bing.com"] += 1
history["youtube.com"] += 1
history["amazon.com"] += 1
history["yahoo.com"] += 1
history["google.com"] += 1
history["wikipedia.com"] += 1
history["wikipedia.com"] += 1
history["yahoo.com"] += 1
history["yahoo.com"] += 1
history["amazon.com"] += 1
print(history.most_common(5))
And this returns:
[('yahoo.com', 5), ('google.com', 2), ('amazon.com', 2), ('wikipedia.com', 2), ('spotify.com', 1)]
Since this comes with a standard installation of Python, I think this should count as "pure" python.
Possible global heap
I am wondering if there is an efficient way to use a global heap
I don't think a global heap could work efficiently. To update a heap when an entry changes (so-called increase-key or decrease-key operations), it suffices to run siftup or siftdown starting at the changed node. However, you first have to find the position of then node within the heap, and we don't have an efficient way to do that.
Alternative data structure
... or something?
There is another way. Create a doubly linked list with links in the form, Link(prev, next, count, url). Track two variables, first and last, to track the endpoints of the doubly linked list. Use a dictionary (hash table) to map from the url to the link entry.
The visit(url) code is responsible for updating the structure.
If the url hasn't been seen before, create a new link, Link(prev=last, next=None, count=1, url), save the link in your site dictionary with site[url] = new_link, append the link the doubly linked list, and update last with last = new_link.
If the url has been seen before, find with link with link = site[url] and update the count with link.count += 1. To maintain the most-common-to-least common sort order, keeping swapping with previous links while link.count > link.prev.count.
This can be optimized further by keeping links with the same count in a pool so that at most one swap will occur.
Whenever printTop5() is called, just traverse the first five entries in the doubly-linked list starting with first.
Working code
from dataclasses import make_dataclass
Link = make_dataclass('Link', ('prev', 'next', 'url', 'count'))
class History:
def __init__(self):
self.site = {}
self.root = root = Link(None, None, 'root', 0)
root.prev = root.next = root
def visit(self, url):
root = self.root
if url in self.site:
link = self.site[url]
link.count += 1
while link.prev is not root and link.prev.count < link.count:
prev = link.prev
prev.prev.next = link
link.next.prev = prev
prev.next, link.next = link.next, prev
prev.prev, link.prev = link, prev.prev
else:
last = root.prev
link = Link(last, root, url, 1)
self.site[url] = root.prev = last.next = link
def printTop5(self):
link = root = self.root
for i in range(5):
if link.next is not root:
link = link.next
print(link.count, link.url)
I'm learning/experimenting with iterators/generators in Python to create a fast search function that can search a large data set for a subset from front to back and back to front simultaneously. I've created a Search Class that I intend to thread its instances so they perform a search in parallel. I am trying to understand the usefulness of generators and want to know if this is a situation where they are in fact useful. The way I have it set up, the dataset must still be loaded into memory. Is there a way to do this where the entire dataset doesn't have to be stored in a variable. Any guidance on my approach to this idea would be greatly appreciated!
class Search:
def __init__(self, dataset):
self.data = dataset
self.index = 0
self.end = len(dataset) - 1
def __iter__(self):
while self.index <= self.end:
yield self.index
self.index += 1
def __reversed__(self):
i = self.end
while i >= self.index:
yield i
i -= 1
def search(self, subset, reverse=False):
switch = (iter(self), reversed(self))
for index in switch[reverse]:
if self.data[index] != subset[0]:
continue
else:
sub = slice(index, index + len(subset))
if self.data[sub] == subset:
result = index
break
else:
result = 'Not Found!'
return result
x = Search(large_data)
#thread_1:
x.search(sublist)
#thread_2:
x.search(sublist, reverse=True)
As I understand your question, you need iteration in the dataset loading function. And here it depends, where does the data come from. For files and databases you will find easy solutions in the documentation. If you are using some web API, you may use pagination.
Python is my first language, and I am very new to it, so the answer may be very clear, but after many hours looking and experimenting, I am not sure what is causing the problem.
An overview of the module:
The DicePool module is meant to manage collections of "dice", stored as dictionary items. Each dictionary key (herein poolKey) has a list containing information about one "type" of dice, most importantly, a tuple describing its faces and an integer representing the quantity of "dice" type 'x' in the "pool."
My specific question regards the Transfer method, which used to be two methods (send and receive, basically), but which I thought I could combine into one method. When the test code at the bottom runs, I'd like it to leave dp.dictPool[poolKey][1] == 0 and dp2.dictPool[poolKey][1] == 2. But in every attempt I've made, the values come out the same. Sorry I can't categorize this question better . . . I don't really know what the problem is.
Anyway, one half of the Transfer method is supposed to run for the "sender" instance, and one half is supposed to run for the "receiver" instance.
import random
class DicePool(object):
def __init__(self):
self.dictPool = {}
def AddDice(self, poolKey, faces = 6, quant = 1, color = "white"):
'''faces must be int or items 'a,b,c'; count must be int or def to 1'''
try: #if count is not an integer, it defaults to 1
quant = int(quant)
except:
print("Quant is not an integer, defaulting to 1")
quant = 1
try: #if faces is can be int, a list is built of numbers 1 to faces
faces = int(faces)
if faces < 2: #a 1 or 0-sided die breaks the program
faces = 2
tempList = []
for i in range(1, faces+1):
tempList.append(i)
faces = tempList
except: #if faces is not an integer, it is split into list items by ","
faces = faces.split(",")
if poolKey in self.dictPool.keys(): #if the key already exists in pool
self.dictPool[poolKey][1] += quant #add to the quantity,
else: #if the key does not already exist, set all attributes
self.dictPool[poolKey] = [faces, quant, color]
def Transfer(self, poolKey, targetPool, sendQuant, senderPool = None):
'''targetPool must be DicePool instance'''
if targetPool:
self.dictPool[poolKey][1] -= sendQuant
targetPool.Transfer(poolKey, None, sendQuant, self)
else:
try:
self.dictPool[poolKey][1] -= sendQuant
except:
self.dictPool[poolKey] = senderPool.dictPool[poolKey]
self.dictPool[poolKey][1] = sendQuant
dp = DicePool()
dp2 = DicePool()
dp.AddDice("d6")
dp.AddDice("d6")
dp.Transfer("d6",dp2,2)
print(dp.dictPool,dp2.dictPool)
The problem is in this line:
self.dictPool[poolKey] = senderPool.dictPool[poolKey]
The values in your dictPools are lists. Here, you set one object's dictPool value to the same list as in the other one. Not a copy of the list, but the same list. So later if you add or subtract from that list, it will affect the other as well, because they are sharing one list object.
Try doing self.dictPool[poolKey] = senderPool.dictPool[poolKey][:]. The [:] grabs the contents of the list rather than the list object iself.
In both parts of the if/else in Transfer you're subtracting the quantity. Don't you want to subtract it in one case but add it in the other?
Is else: try: self.dictPool[poolKey][1] -= sendQuant supposed to be += sendQuant instead?
Might be a semantic issue, not a Python syntax issue.
Here is a version of the Transfer() method that seems to work as intended (more or less), although it's very clumsy looking:
def Transfer(self, poolKey, targetPool, sendQuant, senderPool = None):
'''targetPool must be DicePool instance'''
if isinstance(targetPool, DicePool):
#valid target, so subtract dice here, then call Transfer on target
temp = self.dictPool[poolKey][1:2][0] - sendQuant
print("sent",sendQuant,"leaving",temp)
targetPool.Transfer(poolKey, "Receiver", sendQuant, self)
elif targetPool == "Receiver":
#this executes if a DicePool is named as the targetPool of Transfer
if poolKey in self.dictPool.keys():
self.dictPool[poolKey][1:2][0] += sendQuant
else:
self.dictPool[poolKey] = senderPool.dictPool[poolKey]
self.dictPool[poolKey][1:2] = [sendQuant]
print("now have",self.dictPool[poolKey][1:2][0])
pass
else:
print("Not a valid targetPool, apparently")
File "C:\Users\Tom\Desktop\Tetris!\tetris.py", line 206, in typeSet
Globals.blockArray[i].x.append(7)
IndexError: list index out of range
I get the above error for the 4th line in typeSet
At initialization:
def main():
initialize()
def initialize():
Globals.running = True
addBlock()
class Globals:
running = True
blockArray = []
blockNum = 0
And then later on:
def addBlock():
Globals.blockArray.append(block())
class block:
def __init__(self):
self.id = Globals.blockNum
Globals.blockNum += 1
self.x = []
self.y = []
self.landed = False
self.blockType = 1#random.randint(1,6)
self.typeSet()
def typeSet(self):
i = self.id
if self.blockType == 1:
#square(i)
Globals.blockArray[i].x.append(7)
Globals.blockArray[i].y.append(0)
Globals.blockArray[i].x.append(7)
Globals.blockArray[i].y.append(1)
Globals.blockArray[i].x.append(8)
Globals.blockArray[i].y.append(0)
Globals.blockArray[i].x.append(8)
Globals.blockArray[i].y.append(1)
Edit: added more code and switched it so the id should start at 0. Error code hasn't changed
Not enough code. The error tells you the exact problem. Globals.blockArray does not have a member at position i. That's why you shouldn't work with global variables when you can avoid them, since it can be a hard time making sure your global variables have the expected values in them.
I'm not sure but maybe you want to do
Globals.blockArray.append(self)
in the init function, and also increase Globals.blockNum after the assignment to self.id.
Every time an instance of block is created, Globals.blockNum is incremented by 1 and self.id is set to the current value of Globals.blockNum.
Later (in typeSet) self.id is used to index into Globals.blockArray.
The error occurs when Globals.blockArray doesn't have at least self.id + 1 items in it.
If Globals.blockNum keeps increasing and its value is used (indirectly) to index into Globals.blockArray, this will likely cause the error (unless something causes Globals.blockArray to keep growing too.
While nothing immediate comes to mind looking at your code above, the first thing I would try would be to print the contents of Globals.blockArray, and Globals.blockArray[i].
I am working on a python tetris game that my proffessor assigned for the final project of a concepts of programming class. I have got just about everything he wanted to work on it at this point but I am having a slight problem with one part of it. Whenever I start moving pieces left and right I keep getting "index out of range error". This only happens when it is up against a piece. Here are the culprits that are giving me grief.
def clearRight(block=None):
global board, activeBlock, stackedBlocks
isClear = True
if(block == None):
block = activeBlock
if(block != None):
for square in block['squares']:
row = square[1]
col = square[0]+1
if(col >= 0 and stackedBlocks[row][col] !=None):
isClear=False
return isClear
def clearLeft(block=None):
global board, activeBlock, stackedBlocks
isClear = True
if(block == None):
block = activeBlock
if(block != None):
for square in block['squares']:
row = square[1]
col = square[0]-1
if(col >= 0 and stackedBlocks[row][col] !=None):
isClear=False
return isClear
I am not looking to get anyone to fix it for me, I'm only looking for tips on how to fix it myself. Thanks in advance for any help that is given.
There a typo that would cause that problem in the first method.
When you're checking each cell in the block shifted one right, you don't check if they are off the grid.
if (col >= 0 and ...)
probably should be
if (col < num_cols and ...)
I also agree with CrazyDrummer, make a generic clear function
Spoilers ...
def clear(x_offset, block=None):
if not block:
block = activeBlock
if not block: return True
for x,y in block:
x += x_offset
if not (0 <= x < num_cols) or stackedBlocks[x, y]:
return False
return True
Look at what's different when you're getting the exception. Try printing out program state information to help you zero in. There's only one place where you access an array with variable indexes, so you can narrow your search radius a bit.
Separate suggestion: Make a generic clear that takes determines what direction you want to clear from by the parameters.
I highly recommend the book debugging rules!, it will aid you in searching out and properly fixing problems. :D