diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..72364f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,89 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject diff --git a/check_parentheses.py b/check_parentheses.py new file mode 100644 index 0000000..175dea0 --- /dev/null +++ b/check_parentheses.py @@ -0,0 +1,18 @@ +from stack import Stack + + +def check_parentheses(string): + """Check string for proper parenthetics""" + my_stack = Stack() + for char in string: + if char == '(': + my_stack.push(char) + elif char == ')': + try: + my_stack.pop() + except IndexError: + return -1 + if len(my_stack) == 0: + return 0 + else: + return 1 diff --git a/linked_list.py b/linked_list.py new file mode 100644 index 0000000..c81ea70 --- /dev/null +++ b/linked_list.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- coding: utf -8 -*- + + +class Node(object): + def __init__(self, value): + """Create an instance of Node.""" + self.value = value + self.next_node = None + + +class LinkedList(object): + def __init__(self, param=None): + """Create an instance of LinkedList. + The optional parameter needs to be an itarable.""" + self.head_node = None + self.length_list = 0 + if isinstance(param, list): + for i in param: + self.push(i) + elif param is not None: + self.push(param) + + def push(self, val): + """Insert a new node to the head of a linked list.""" + new_node = Node(val) + new_node.next_node = self.head_node + self.head_node = new_node + self.length_list += 1 + return self.head_node + + def __len__(self): + """Return the length of the linked list for the built-in len.""" + return self.length_list + + def size(self): + """Return the length of the linked list.""" + return len(self) + + def search(self, val): + """Search for a node based on the val.""" + current_node = self.head_node + flag = True + while current_node and flag: + if current_node.value != val: + current_node = current_node.next_node + else: + flag = False + return current_node + + def remove(self, val): + """Remove a node from linked list.""" + current_node = self.head_node + previous_node = None + while current_node: + if current_node.value == val: + if not previous_node: + self.head_node = current_node.next_node + else: + previous_node.next_node = current_node.next_node + self.length_list -= 1 + return True + else: + previous_node = current_node + current_node = current_node.next_node + return False + + def pop(self): + """Remove a node from the head, return removed node value.""" + if self.length_list == 0: + raise IndexError('empty list') + popped_node = self.head_node + self.head_node = self.head_node.next_node + self.length_list -= 1 + return popped_node + + def display(self): + """Display a linked list as a tuple.""" + current_node = self.head_node + display_tuple = '(' if current_node is not None else '()' + while current_node: + if not current_node.next_node: + display_tuple += str(current_node.value) + ')' + else: + display_tuple += str(current_node.value) + ', ' + current_node = current_node.next_node + return display_tuple diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..8e21537 --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +# -*- coding: utf -8*- +from setuptools import setup + +setup( + name="Proper Parenthetics", + description="Proper Parenthetics for code challenge", + version=0.1, + license='MIT', + author="Steven Than", + author_email="steventhan11@gmail.com", + py_modules=['linked_list', 'stack', 'check_parentheses'], + package_dir={' ': '.'}, + install_requires=[], + extras_require={'test': ['pytest', 'pytest-cov', 'tox']}, +) diff --git a/stack.py b/stack.py new file mode 100644 index 0000000..00672b3 --- /dev/null +++ b/stack.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf -8 -*- + +from linked_list import LinkedList + + +class Stack(object): + def __init__(self, param=None): + """Create an instance of Stack. + This is a composition with linked list.""" + self._parent = LinkedList(param) + + def __len__(self): + """Return the length of the linked list for the built-in len.""" + return self._parent.__len__() + + def pop(self): + """Remove a node from the head, return removed node.""" + return self._parent.pop() + + def push(self, val): + """Insert a new node to the head of a stack.""" + return self._parent.push(val) diff --git a/test_check_parentheses.py b/test_check_parentheses.py new file mode 100644 index 0000000..a8d58e3 --- /dev/null +++ b/test_check_parentheses.py @@ -0,0 +1,17 @@ +import pytest + +PARENTHESES_TABLE = [ + ('(this is a properly closed (string))', 0), + ('string(str)string(str)str))', -1), + ('(str((str', 1), + ('()', 0), + ('(', 1), + (')', -1) +] + + +@pytest.mark.parametrize('string, result', PARENTHESES_TABLE) +def test_check_parentheses(string, result): + """Test check_parentheses func with multiple params""" + from check_parentheses import check_parentheses + assert check_parentheses(string) == result diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..30933c2 --- /dev/null +++ b/tox.ini @@ -0,0 +1,6 @@ +[tox] +envlist = py27, py35 +[testenv] +commands = py.test +deps = + pytest