-
Notifications
You must be signed in to change notification settings - Fork 2
Proper parenthetics for jay-tyler *DO NOT MERGE* #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
65a78c5
bb15d80
024a7f4
0208fd5
bb02fd2
26860d9
cc64a2e
c8d95e5
4df0f92
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| #Parenthetics | ||
| Parenthetics includes an eponymous function which takes a string | ||
| argument and determines whether or not it has broken, open, or well-formed | ||
| parentheses. | ||
|
|
||
| ##Illustrative Examples of functionality | ||
| These are all simplified examples. Any unicode characters can be | ||
| intersperced into any of the following. | ||
|
|
||
| ###Case One: Broken Parentheses | ||
|
|
||
| All of the following arguments will return -1: | ||
| ``` | ||
| >>> parenthetical("))))(((") | ||
| >>> parenthetical(")") | ||
| >>> parenthetical("()()()()()))(()))()(((()") | ||
| ``` | ||
| ###Case Two: Open Parentheses | ||
|
|
||
| All of the following will return 1: | ||
| ``` | ||
| >>> parenthetical("()(") | ||
| >>> parenthetical("()()()(") | ||
| >>> parenthetical("()()(((()()()") | ||
| ``` | ||
|
|
||
| ###Case Three: Okay Parentheses | ||
| All of the following will return 0: | ||
| ``` | ||
| >>> parenthetical("()()()()") | ||
| >>> parenthetical("(())") | ||
| >>> parenthetical("((()())()()())") | ||
| ``` | ||
|
|
||
| ##Helpful Resources | ||
| All of the following were helpful in constructing this code: | ||
| * [Filtering using a set constructor] | ||
| (http://stackoverflow.com/questions/3013449/list-filtering-list-comprehension-vs-lambda-filter) | ||
| * [Why isn't there a sign function in Python?] | ||
| (http://stackoverflow.com/questions/1986152/why-python-doesnt-have-a-sign-function) | ||
| * [Goose-typing is intended in Python] | ||
| (https://docs.python.org/2/glossary.html#term-abstract-base-class) | ||
|
|
||
| The "goose typing" coin was termed in an amusing little article by | ||
| Alex Martelli included as part of Luciano Rahmalho's Fluent Python. | ||
| I'm tempted to do a lightining talk on it. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| from __future__ import unicode_literals | ||
| from random import choice as choice | ||
| from abc import types | ||
| import math | ||
|
|
||
| def generate_parenthetical_iterable(string): | ||
| """ | ||
| Take a string and return an ordered iterable with only the "(" and ")" | ||
| characters remaining | ||
| """ | ||
| # Using an abstract base class to "goose-type" check; | ||
| # this is an intentional part of the Python language. See README.md | ||
| if not isinstance(string, types.StringTypes): | ||
| raise TypeError | ||
|
|
||
| set_to_find = ["(", ")"] #Defining a filter | ||
| characters = tuple(string) #Turning characters into an iterable | ||
|
|
||
| for character in characters: | ||
| if character in set_to_find: | ||
| yield character | ||
|
|
||
|
|
||
|
|
||
| def parenthetical(string): | ||
| """ | ||
| Examine a string for closed, open, and well-formed parentheses; | ||
| return a -1, 1, and 0 respectively. | ||
|
|
||
| It might be helpful to recall that parenthesis is of greek etymology; | ||
| parenthesis is singular, parentheses plural. | ||
| """ | ||
|
|
||
| parentheses = generate_parenthetical_iterable(string) | ||
|
|
||
| # Score will help us keep track of parentheses state as we iterate; | ||
| # also will allow us to short-circuit out of for loop for open parenthesis | ||
| score = 0 | ||
|
|
||
| for parenthesis in parentheses: | ||
| if parenthesis == ")": | ||
| score -= 1 | ||
| if score < 0: | ||
| # An open parenthesis exists. No need to check further. | ||
| break | ||
| else: | ||
| # Parenthesis is "(" here | ||
| score += 1 | ||
|
|
||
| if score in set([1, 0, -1]): | ||
| # Score can be directly returned in some cases | ||
| return score | ||
|
|
||
| else: | ||
| # Else use copysign to transfer sign of score to 1 | ||
| return math.copysign(1, score) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| from __future__ import unicode_literals | ||
| import pytest | ||
| from parenthetics import parenthetical | ||
|
|
||
|
|
||
| # Case that should return -1 | ||
| broken_case = [ | ||
| "))sdf)as((43215(" | ||
| ")tqw()3", | ||
| "345a)))", | ||
| "eq()()q()hq()(hqre[][][]{()))", | ||
| ")dfh", | ||
| "ehu{()()())()()()()", | ||
| "({})()()()()))asdg(())asgwq)()321t5(((()12fds"] | ||
|
|
||
| # Case that should return 1 | ||
| open_case = [ | ||
| "!@#^$#&(324643)(", | ||
| "()asdgw()(@!#&^$#&)(", | ||
| "13246((()asd()oigo", | ||
| "$@*-=()()(", | ||
| "(qy1235)()(qwet)((", | ||
| "()()((3461(()25()153()145" | ||
| ] | ||
|
|
||
| # Case that should return 0 | ||
| okay_case = [ | ||
| "(1266)()32164()!$#^%@&()|||", | ||
| "((qwet)qwet)", | ||
| "(52135(()1231())()q143265123()())" | ||
| ] | ||
|
|
||
| # Types that should fail with TypeError | ||
| bad_types = [ | ||
| 0, | ||
| None, | ||
| 5.32324, | ||
| {1: 123123, "a": 45243}, | ||
| [1, 2, 3, 4, 5] | ||
| ] | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not turn all these into fixtures? |
||
|
|
||
| def test_broken_case_via_assert(broken_case=broken_case): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This syntax is not really all that helpful to you. The broken_case symbol is bound in the scope of this function, but only because it's bound in the module as well. This does not operate the way you are thinking it does. I believe what you were looking for is the "fixture" approach we used in the learning journal tests. Do you remember how that worked? Can you apply the same approach here?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I started out with fixtures, but then opted to do lists because it was a tad simpler looking (momentarily opting for kiss). But the scope control is a good point, I wasn't thinking about that. Thanks! |
||
| """ | ||
| Testing a set of arguments that should return -1 | ||
| """ | ||
| for case in broken_case: | ||
| assert parenthetical(case) == -1 | ||
|
|
||
|
|
||
| def test_open_case_via_assert(open_case=open_case): | ||
| """ | ||
| Testing a set of arguments that should return 1 | ||
| """ | ||
| for case in open_case: | ||
| assert parenthetical(case) == 1 | ||
|
|
||
|
|
||
| def test_okay_case_via_assert(okay_case=okay_case): | ||
| """ | ||
| Testing a set of arguments that should return 1 | ||
| """ | ||
| for case in okay_case: | ||
| assert parenthetical(case) == 0 | ||
|
|
||
|
|
||
| def test_bad_types(bad_types=bad_types): | ||
| """ | ||
| Testing types that are not currently supported by parenthetical; | ||
| these will raise TypeError | ||
| """ | ||
| for bad_type in bad_types: | ||
| with pytest.raises(TypeError): | ||
| parenthetical(bad_type) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow. This is the most complex implementation of a solution to this problem I've seen yet. You've used a lot of tools. And used them correctly. However, it could be a lot simpler with the use of a data structure you learned this week.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed the comment you made about using Stacks in lecture... oh well, it was actually kind of fun doing it this way.