diff --git a/solutions/object_oriented_design/circular_array/Circular array.ipynb b/solutions/object_oriented_design/circular_array/Circular array.ipynb new file mode 100644 index 00000000..3198b044 --- /dev/null +++ b/solutions/object_oriented_design/circular_array/Circular array.ipynb @@ -0,0 +1,105 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints and Assumptions\n", + "> Can we assume the list fits in memory?\n", + "\n", + "Yes\n", + "\n", + "> Do we have to worry about load factors?\n", + "\n", + "No\n", + "\n", + "> Can we assume inputs are valid or do we have to validate them?\n", + "\n", + "Assume they are valid" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class CircularArray:\n", + " \"\"\"\n", + " Python class implementing a circular array.\n", + " \"\"\"\n", + " def __init__(self, size):\n", + " \"\"\"\n", + " Constructor. Refer to `full()` for explanation for `size+1`.\n", + " \"\"\"\n", + " self._size = size + 1\n", + " self._items = [None for _ in range(self._size)]\n", + " self._read = 0 # read pointer\n", + " self._write = 0 # write pointer\n", + " \n", + " @property\n", + " def empty(self):\n", + " \"\"\"\n", + " Returns whether the queue is empty.\n", + " \"\"\"\n", + " return self._read == self._write\n", + " \n", + " @property\n", + " def full(self):\n", + " \"\"\"\n", + " Return wheter the queue is full.\n", + " In a naive implementation, full and empty are both indicated by read == write, which introduces an\n", + " ambiguity. Thus, to break this, a circular array is assumed to be full if the write pointer is one\n", + " position behind the read pointer. To preserve the inituitiveness of the class interface, the internal\n", + " size property is incremented by 1, a sentinel element (so this way the user won't be surprised that their\n", + " array that supposedly fits 10 elements only accomodates 9).\n", + " \"\"\"\n", + " return self._read == (self._write + 1) % self._size\n", + " \n", + " def queue(self, item):\n", + " \"\"\"\n", + " Enqueues an item in the circular buffer.\n", + " \"\"\"\n", + " if self.full:\n", + " raise BufferError('Queue is full')\n", + " self._items[self._write] = item\n", + " self._write = (self._write + 1) % self._size\n", + " \n", + " def dequeue(self):\n", + " \"\"\"\n", + " Dequeues an item from the buffer.\n", + " \"\"\"\n", + " if self.empty:\n", + " raise BufferError('Queue is empty')\n", + " item = self._items[self._read]\n", + " self._items[self._read] = None\n", + " self._read = (self._read + 1) % self._size\n", + " return item" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python [py35]", + "language": "python", + "name": "Python [py35]" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/object_oriented_design/circular_array/__init__.py b/solutions/object_oriented_design/circular_array/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/solutions/object_oriented_design/circular_array/circular_array.py b/solutions/object_oriented_design/circular_array/circular_array.py new file mode 100644 index 00000000..8d0db73a --- /dev/null +++ b/solutions/object_oriented_design/circular_array/circular_array.py @@ -0,0 +1,51 @@ +class CircularArray: + """ + Python class implementing a circular array. + """ + def __init__(self, size): + """ + Constructor. Refer to `full()` for explanation for `size+1`. + """ + self._size = size + 1 + self._items = [None for _ in range(self._size)] + self._read = 0 # read pointer + self._write = 0 # write pointer + + @property + def empty(self): + """ + Returns whether the queue is empty. + """ + return self._read == self._write + + @property + def full(self): + """ + Return wheter the queue is full. + In a naive implementation, full and empty are both indicated by read == write, which introduces an + ambiguity. Thus, to break this, a circular array is assumed to be full if the write pointer is one + position behind the read pointer. To preserve the inituitiveness of the class interface, the internal + size property is incremented by 1, a sentinel element (so this way the user won't be surprised that their + array that supposedly fits 10 elements only accomodates 9). + """ + return self._read == (self._write + 1) % self._size + + def queue(self, item): + """ + Enqueues an item in the circular buffer. + """ + if self.full: + raise BufferError('Queue is full') + self._items[self._write] = item + self._write = (self._write + 1) % self._size + + def dequeue(self): + """ + Dequeues an item from the buffer. + """ + if self.empty: + raise BufferError('Queue is empty') + item = self._items[self._read] + self._items[self._read] = None + self._read = (self._read + 1) % self._size + return item