Skip to content
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

custom-set: implement exercise #1140

Merged
merged 10 commits into from
Jan 19, 2018
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,16 @@
"object_oriented_programming"
]
},
{
"uuid": "bb07c236-062c-2980-483a-a221e4724445dcd6f32",
"slug": "custom-set",
"core": false,
"unlocked_by": null,
"difficulty": 5,
"topics": [
"sets"
]
},
{
"uuid": "e7351e8e-d3ff-4621-b818-cd55cf05bffd",
"slug": "accumulate",
Expand Down
20 changes: 20 additions & 0 deletions exercises/custom-set/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Custom-Set

Create a custom set type.

Sometimes it is necessary to define a custom data structure of some
type, like a set. In this exercise you will define your own set. How it
works internally doesn't matter, as long as it behaves like a set of
unique elements.

## Submitting Exercises

Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.

For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.

For more detailed information about running tests, code style and linting,
please see the [help page](http://exercism.io/languages/python).

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
30 changes: 30 additions & 0 deletions exercises/custom-set/custom_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class CustomSet(object):
def __init__(self, elements=[]):
pass

def empty(self):
pass

def __iter__(self):
pass

def subset(self, other):
pass

def disjoint(self, other):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be isdisjoint and issubset to match with Python's implementation of set. I would also then change empty to isempty for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

pass

def __eq__(self, other):
pass

def add(self, element):
pass

def intersection(self, other):
pass

def difference(self, other):
pass

def union(self, other):
pass
212 changes: 212 additions & 0 deletions exercises/custom-set/custom_set_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import unittest

from custom_set import CustomSet


# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0

class CustomSetTest(unittest.TestCase):
def test_sets_with_no_elements_are_empty(self):
sut = CustomSet()
self.assertIs(sut.empty(), True)

def test_sets_with_elements_are_not_empty(self):
sut = CustomSet([1])
self.assertIs(sut.empty(), False)

def test_empty_set_contains_nothing(self):
sut = CustomSet()
self.assertNotIn(1, sut)

def test_set_contains_when_element_in_set(self):
sut = CustomSet([1])
self.assertIn(1, sut)

def test_set_does_not_contains_when_element_not_in_set(self):
sut = CustomSet([1, 2, 3])
self.assertNotIn(4, sut)

def test_empty_set_is_subset_of_another_empty_set(self):
set1 = CustomSet()
set2 = CustomSet()
self.assertIs(set1.subset(set2), True)

def test_empty_set_is_subset_of_non_empty_set(self):
set1 = CustomSet()
set2 = CustomSet([1])
self.assertIs(set1.subset(set2), True)

def test_non_empty_set_is_not_subet_of_empty_set(self):
set1 = CustomSet([1])
set2 = CustomSet()
self.assertIs(set1.subset(set2), False)

def test_set_is_subset_of_set_with_exact_same_elements(self):
set1 = CustomSet([1, 2, 3])
set2 = CustomSet([1, 2, 3])
self.assertIs(set1.subset(set2), True)

def test_set_is_subset_of_larger_set_with_same_elements(self):
set1 = CustomSet([1, 2, 3])
set2 = CustomSet([4, 1, 2, 3])
self.assertIs(set1.subset(set2), True)

def test_set_not_subset_of_set_that_does_not_contain_its_elements(self):
set1 = CustomSet([1, 2, 3])
set2 = CustomSet([4, 1, 3])
self.assertIs(set1.subset(set2), False)

def test_empty_set_disjoint_with_itself(self):
set1 = CustomSet()
set2 = CustomSet()
self.assertIs(set1.disjoint(set2), True)

def test_empty_set_disjoint_with_non_empty_set(self):
set1 = CustomSet()
set2 = CustomSet([1])
self.assertIs(set1.disjoint(set2), True)

def test_non_empty_set_disjoint_with_empty_set(self):
set1 = CustomSet([1])
set2 = CustomSet()
self.assertIs(set1.disjoint(set2), True)

def test_sets_not_disjoint_if_element_is_shared(self):
set1 = CustomSet([1, 2])
set2 = CustomSet([2, 3])
self.assertIs(set1.disjoint(set2), False)

def test_sets_disjoint_if_not_elements_are_shared(self):
set1 = CustomSet([1, 2])
set2 = CustomSet([3, 4])
self.assertIs(set1.disjoint(set2), True)

def test_empty_sets_are_equal(self):
set1 = CustomSet()
set2 = CustomSet()
self.assertEqual(set1, set2)

def test_empty_set_not_equal_to_non_empty_set(self):
set1 = CustomSet()
set2 = CustomSet([1, 2, 3])
self.assertNotEqual(set1, set2)

def test_non_empty_set_not_equal_to_empty_set(self):
set1 = CustomSet([1, 2, 3])
set2 = CustomSet()
self.assertNotEqual(set1, set2)

def test_sets_with_same_exact_same_elements_are_equal(self):
set1 = CustomSet([1, 2])
set2 = CustomSet([2, 1])
self.assertEqual(set1, set2)

def test_sets_with_different_elements_are_not_equal(self):
set1 = CustomSet([1, 2, 3])
set2 = CustomSet([1, 2, 4])
self.assertNotEqual(set1, set2)

def test_set_is_not_equal_to_larger_set_with_same_elements(self):
set1 = CustomSet([1, 2, 3])
set2 = CustomSet([1, 2, 3, 4])
self.assertNotEqual(set1, set2)

def test_add_to_empty_set(self):
sut = CustomSet()
sut.add(1)
expected = CustomSet([1])
self.assertEqual(sut, expected)

def test_add_to_non_empty_set(self):
sut = CustomSet([1, 2, 4])
sut.add(3)
expected = CustomSet([1, 2, 3, 4])
self.assertEqual(sut, expected)

def test_adding_existing_element_does_not_change_set(self):
sut = CustomSet([1, 2, 3])
sut.add(3)
expected = CustomSet([1, 2, 3])
self.assertEqual(sut, expected)

def test_intersection_of_two_empty_sets_is_empty_set(self):
set1 = CustomSet()
set2 = CustomSet()
expected = CustomSet()
self.assertEqual(set1.intersection(set2), expected)

def test_intersection_of_empty_set_and_non_empty_set_is_empty_set(self):
set1 = CustomSet()
set2 = CustomSet([3, 2, 5])
expected = CustomSet()
self.assertEqual(set1.intersection(set2), expected)

def test_intersection_of_non_empty_set_and_empty_set_is_empty_set(self):
set1 = CustomSet([1, 2, 3, 4])
set2 = CustomSet()
expected = CustomSet()
self.assertEqual(set1.intersection(set2), expected)

def test_intersection_of_sets_with_no_shared_elements_is_empty_set(self):
set1 = CustomSet([1, 2, 3])
set2 = CustomSet([4, 5, 6])
expected = CustomSet()
self.assertEqual(set1.intersection(set2), expected)

def test_intersection_contains_shared_elements_only(self):
set1 = CustomSet([1, 2, 3, 4])
set2 = CustomSet([3, 2, 5])
expected = CustomSet([2, 3])
self.assertEqual(set1.intersection(set2), expected)

def test_difference_of_two_empty_sets_is_empty_set(self):
set1 = CustomSet()
set2 = CustomSet()
expected = CustomSet()
self.assertEqual(set1 - set2, expected)

def test_difference_of_empty_set_and_non_empty_set_is_empty_set(self):
set1 = CustomSet()
set2 = CustomSet([3, 2, 5])
expected = CustomSet()
self.assertEqual(set1 - set2, expected)

def test_difference_of_non_empty_set_and_empty_set_is_non_empty_set(self):
set1 = CustomSet([1, 2, 3, 4])
set2 = CustomSet()
expected = CustomSet([1, 2, 3, 4])
self.assertEqual(set1 - set2, expected)

def test_difference_of_non_empty_sets_elements_in_first_set_only(self):
set1 = CustomSet([3, 2, 1])
set2 = CustomSet([2, 4])
expected = CustomSet([1, 3])
self.assertEqual(set1 - set2, expected)

def test_union_of_empty_sets_is_empty_set(self):
set1 = CustomSet()
set2 = CustomSet()
expected = CustomSet()
self.assertEqual(set1 + set2, expected)

def test_union_of_empty_set_and_non_empty_set_is_the_non_empty_set(self):
set1 = CustomSet()
set2 = CustomSet([2])
expected = CustomSet([2])
self.assertEqual(set1 + set2, expected)

def test_union_of_non_empty_set_and_empty_set_is_the_non_empty_set(self):
set1 = CustomSet([1, 3])
set2 = CustomSet()
expected = CustomSet([1, 3])
self.assertEqual(set1 + set2, expected)

def test_union_of_non_empty_sets_contains_all_unique_elements(self):
set1 = CustomSet([1, 3])
set2 = CustomSet([2, 3])
expected = CustomSet([1, 2, 3])
self.assertEqual(set1 + set2, expected)


if __name__ == '__main__':
unittest.main()
42 changes: 42 additions & 0 deletions exercises/custom-set/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class CustomSet(object):
def __init__(self, elements=[]):
self.elements = list(elements)

def empty(self):
return not any(self.elements)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be return not self.elements - the current version would wrongly return True if self.elements == [0]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch... fixing.


def __iter__(self):
return iter(self.elements)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__iter__ is a kind of useful method to implement here, but I think that __contains__ would be a more logical for testing whether a set contains a particular value. Presumably __iter__ is providing that functionality here, but from a learner point of view, I don't think that this would be clear enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I somehow was not aware of the __contains__ method previously, and though that implementing __iter__ was the correct way of supporting the in operator... I will fix this, thanks!


def subset(self, other):
return all(x in other for x in self)

def disjoint(self, other):
return all(x not in other for x in self)

def __eq__(self, other):
return self.subset(other) and other.subset(self)

def add(self, element):
if element not in self:
self.elements.append(element)

def intersection(self, other):
result = CustomSet()
for x in self:
if x in other:
result.add(x)
return result

def __sub__(self, other):
result = CustomSet()
for x in self:
if x not in other:
result.add(x)
return result

def __add__(self, other):
result = CustomSet(self.elements)
for x in other:
result.add(x)
return result