[v3] Move existing exercises to exercises/practice
This commit is contained in:
committed by
Corey McCandless
parent
ce102a08af
commit
c5d9d9c0b3
10
exercises/practice/simple-linked-list/.meta/hints.md
Normal file
10
exercises/practice/simple-linked-list/.meta/hints.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## Hints
|
||||
|
||||
To support `list()`, see [implementing an iterator for a class.](https://docs.python.org/3/tutorial/classes.html#iterators)
|
||||
|
||||
Additionally, note that Python2's `next()` has been replaced by `__next__()` in Python3. For dual compatibility, `next()` can be implemented as:
|
||||
|
||||
```Python
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
```
|
||||
80
exercises/practice/simple-linked-list/README.md
Normal file
80
exercises/practice/simple-linked-list/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Simple Linked List
|
||||
|
||||
Write a simple linked list implementation that uses Elements and a List.
|
||||
|
||||
The linked list is a fundamental data structure in computer science,
|
||||
often used in the implementation of other data structures. They're
|
||||
pervasive in functional programming languages, such as Clojure, Erlang,
|
||||
or Haskell, but far less common in imperative languages such as Ruby or
|
||||
Python.
|
||||
|
||||
The simplest kind of linked list is a singly linked list. Each element in the
|
||||
list contains data and a "next" field pointing to the next element in the list
|
||||
of elements.
|
||||
|
||||
This variant of linked lists is often used to represent sequences or
|
||||
push-down stacks (also called a LIFO stack; Last In, First Out).
|
||||
|
||||
As a first take, lets create a singly linked list to contain the range (1..10),
|
||||
and provide functions to reverse a linked list and convert to and from arrays.
|
||||
|
||||
When implementing this in a language with built-in linked lists,
|
||||
implement your own abstract data type.
|
||||
|
||||
## Hints
|
||||
|
||||
To support `list()`, see [implementing an iterator for a class.](https://docs.python.org/3/tutorial/classes.html#iterators)
|
||||
|
||||
Additionally, note that Python2's `next()` has been replaced by `__next__()` in Python3. For dual compatibility, `next()` can be implemented as:
|
||||
|
||||
```Python
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
```
|
||||
|
||||
|
||||
## Exception messages
|
||||
|
||||
Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to
|
||||
indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not
|
||||
every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include
|
||||
a message.
|
||||
|
||||
To raise a message with an exception, just write it as an argument to the exception type. For example, instead of
|
||||
`raise Exception`, you should write:
|
||||
|
||||
```python
|
||||
raise Exception("Meaningful message indicating the source of the error")
|
||||
```
|
||||
|
||||
## Running the tests
|
||||
|
||||
To run the tests, run `pytest simple_linked_list_test.py`
|
||||
|
||||
Alternatively, you can tell Python to run the pytest module:
|
||||
`python -m pytest simple_linked_list_test.py`
|
||||
|
||||
### Common `pytest` options
|
||||
|
||||
- `-v` : enable verbose output
|
||||
- `-x` : stop running tests on first failure
|
||||
- `--ff` : run failures from previous test before running other test cases
|
||||
|
||||
For other options, see `python -m pytest -h`
|
||||
|
||||
## Submitting Exercises
|
||||
|
||||
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/simple-linked-list` directory.
|
||||
|
||||
You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`.
|
||||
|
||||
For more detailed information about running tests, code style and linting,
|
||||
please see [Running the Tests](http://exercism.io/tracks/python/tests).
|
||||
|
||||
## Source
|
||||
|
||||
Inspired by 'Data Structures and Algorithms with Object-Oriented Design Patterns in Ruby', singly linked-lists. [http://www.brpreiss.com/books/opus8/html/page96.html#SECTION004300000000000000000](http://www.brpreiss.com/books/opus8/html/page96.html#SECTION004300000000000000000)
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||
67
exercises/practice/simple-linked-list/example.py
Normal file
67
exercises/practice/simple-linked-list/example.py
Normal file
@@ -0,0 +1,67 @@
|
||||
class Node:
|
||||
def __init__(self, value):
|
||||
self._value = value
|
||||
self._next = None
|
||||
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
def next(self):
|
||||
return self._next
|
||||
|
||||
|
||||
class LinkedIterator:
|
||||
def __init__(self, linked_list):
|
||||
self.current = linked_list._head
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.current is None:
|
||||
raise StopIteration
|
||||
value = self.current.value()
|
||||
self.current = self.current.next()
|
||||
return value
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
|
||||
class LinkedList:
|
||||
def __init__(self, values=[]):
|
||||
self._head = None
|
||||
self._len = 0
|
||||
[self.push(v) for v in values]
|
||||
|
||||
def __iter__(self):
|
||||
return LinkedIterator(self)
|
||||
|
||||
def __len__(self):
|
||||
return self._len
|
||||
|
||||
def head(self):
|
||||
if self._head is None:
|
||||
raise EmptyListException("The list is empty")
|
||||
return self._head
|
||||
|
||||
def push(self, value):
|
||||
newNode = Node(value)
|
||||
newNode._next = self._head
|
||||
self._head = newNode
|
||||
self._len += 1
|
||||
|
||||
def pop(self):
|
||||
if self._head is None:
|
||||
raise EmptyListException("The list is empty")
|
||||
self._len -= 1
|
||||
ret = self._head.value()
|
||||
self._head = self._head.next()
|
||||
return ret
|
||||
|
||||
def reversed(self):
|
||||
return LinkedList(self)
|
||||
|
||||
|
||||
class EmptyListException(Exception):
|
||||
pass
|
||||
33
exercises/practice/simple-linked-list/simple_linked_list.py
Normal file
33
exercises/practice/simple-linked-list/simple_linked_list.py
Normal file
@@ -0,0 +1,33 @@
|
||||
class Node:
|
||||
def __init__(self, value):
|
||||
pass
|
||||
|
||||
def value(self):
|
||||
pass
|
||||
|
||||
def next(self):
|
||||
pass
|
||||
|
||||
|
||||
class LinkedList:
|
||||
def __init__(self, values=[]):
|
||||
pass
|
||||
|
||||
def __len__(self):
|
||||
pass
|
||||
|
||||
def head(self):
|
||||
pass
|
||||
|
||||
def push(self, value):
|
||||
pass
|
||||
|
||||
def pop(self):
|
||||
pass
|
||||
|
||||
def reversed(self):
|
||||
pass
|
||||
|
||||
|
||||
class EmptyListException(Exception):
|
||||
pass
|
||||
116
exercises/practice/simple-linked-list/simple_linked_list_test.py
Normal file
116
exercises/practice/simple-linked-list/simple_linked_list_test.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import unittest
|
||||
|
||||
from simple_linked_list import LinkedList, EmptyListException
|
||||
|
||||
|
||||
# No canonical data available for this exercise
|
||||
|
||||
class SimpleLinkedListTest(unittest.TestCase):
|
||||
def test_empty_list_has_len_zero(self):
|
||||
sut = LinkedList()
|
||||
self.assertEqual(len(sut), 0)
|
||||
|
||||
def test_singleton_list_has_len_one(self):
|
||||
sut = LinkedList([1])
|
||||
self.assertEqual(len(sut), 1)
|
||||
|
||||
def test_non_empty_list_has_correct_len(self):
|
||||
sut = LinkedList([1, 2, 3])
|
||||
self.assertEqual(len(sut), 3)
|
||||
|
||||
def test_error_on_empty_list_head(self):
|
||||
sut = LinkedList()
|
||||
with self.assertRaisesWithMessage(EmptyListException):
|
||||
sut.head()
|
||||
|
||||
def test_singleton_list_has_head(self):
|
||||
sut = LinkedList([1])
|
||||
self.assertEqual(sut.head().value(), 1)
|
||||
|
||||
def test_non_empty_list_has_correct_head(self):
|
||||
sut = LinkedList([1, 2])
|
||||
self.assertEqual(sut.head().value(), 2)
|
||||
|
||||
def test_can_push_to_non_empty_list(self):
|
||||
sut = LinkedList([1, 2, 3])
|
||||
sut.push(4)
|
||||
self.assertEqual(len(sut), 4)
|
||||
|
||||
def test_pushing_to_empty_list_changes_head(self):
|
||||
sut = LinkedList()
|
||||
sut.push(5)
|
||||
self.assertEqual(len(sut), 1)
|
||||
self.assertEqual(sut.head().value(), 5)
|
||||
|
||||
def test_can_pop_from_non_empty_list(self):
|
||||
sut = LinkedList([3, 4, 5])
|
||||
self.assertEqual(sut.pop(), 5)
|
||||
self.assertEqual(len(sut), 2)
|
||||
self.assertEqual(sut.head().value(), 4)
|
||||
|
||||
def test_pop_from_singleton_list_removes_head(self):
|
||||
sut = LinkedList([1])
|
||||
self.assertEqual(sut.pop(), 1)
|
||||
with self.assertRaisesWithMessage(EmptyListException):
|
||||
sut.head()
|
||||
|
||||
def test_error_on_empty_list_pop(self):
|
||||
sut = LinkedList()
|
||||
with self.assertRaisesWithMessage(EmptyListException):
|
||||
sut.pop()
|
||||
|
||||
def test_push_and_pop(self):
|
||||
sut = LinkedList([1, 2])
|
||||
sut.push(3)
|
||||
self.assertEqual(len(sut), 3)
|
||||
self.assertEqual(sut.pop(), 3)
|
||||
self.assertEqual(sut.pop(), 2)
|
||||
self.assertEqual(sut.pop(), 1)
|
||||
self.assertEqual(len(sut), 0)
|
||||
sut.push(4)
|
||||
self.assertEqual(len(sut), 1)
|
||||
self.assertEqual(sut.head().value(), 4)
|
||||
|
||||
def test_singleton_list_head_has_no_next(self):
|
||||
sut = LinkedList([1])
|
||||
self.assertIsNone(sut.head().next())
|
||||
|
||||
def test_non_empty_list_traverse(self):
|
||||
sut = LinkedList(range(10))
|
||||
current = sut.head()
|
||||
for i in range(10):
|
||||
self.assertEqual(current.value(), 9 - i)
|
||||
current = current.next()
|
||||
self.assertIsNone(current)
|
||||
|
||||
def test_empty_linked_list_to_list_is_empty(self):
|
||||
sut = LinkedList()
|
||||
self.assertEqual(list(sut), [])
|
||||
|
||||
def test_singleton_linked_list_to_list_list_with_singular_element(self):
|
||||
sut = LinkedList([1])
|
||||
self.assertEqual(list(sut), [1])
|
||||
|
||||
def test_non_empty_linked_list_to_list_is_list_with_all_elements(self):
|
||||
sut = LinkedList([1, 2, 3])
|
||||
self.assertEqual(list(sut), [3, 2, 1])
|
||||
|
||||
def test_reversed_empty_list_is_empty_list(self):
|
||||
sut = LinkedList([])
|
||||
self.assertEqual(list(sut.reversed()), [])
|
||||
|
||||
def test_reversed_singleton_list_is_same_list(self):
|
||||
sut = LinkedList([1])
|
||||
self.assertEqual(list(sut.reversed()), [1])
|
||||
|
||||
def test_reverse_non_empty_list(self):
|
||||
sut = LinkedList([1, 2, 3])
|
||||
self.assertEqual(list(sut.reversed()), [1, 2, 3])
|
||||
|
||||
# Utility functions
|
||||
def assertRaisesWithMessage(self, exception):
|
||||
return self.assertRaisesRegex(exception, r".+")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user