diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f417c76..24b4717 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: pass_filenames: false types: [python] - # the following was adapted from https://github.com/astral-sh/ruff-pre-commit + # ruff hooks were adapted from https://github.com/astral-sh/ruff-pre-commit - id: lint name: ruff-check entry: poetry run ruff check --force-exclude @@ -30,3 +30,10 @@ repos: language: system types_or: [python, pyi] require_serial: true + + - id: docs + name: pdoc + entry: poetry run pdoc --docformat markdown -o docs paddles + language: system + pass_filenames: false + types: [python] diff --git a/LICENSE b/LICENSE index 3f75c4b..dc5bc12 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2023, The Open University +Copyright (c) 2024, The Open University All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 66d3b17..a638874 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,141 @@ `paddles` aims to be an algorithms and data structures library in Python that - is thoroughly tested and documented - is easy to install, use and understand -- adheres to Python's coding style. +- adheres to good coding practices. + +To learn what the library provides and how to use it, see https://dsa-ou.github.io/paddles. + +The rest of this file documents how the library is developed, +in case you want to contribute. + +## Licence +The code and text in this repository are Copyright © 2024 by The Open University, UK. +The code is licensed under a [BSD 3-clause licence](LICENSE.MD). +The text is licensed under a +[Creative Commons Attribution 4.0 International Licence](http://creativecommons.org/licenses/by/4.0). + +By contributing code or documentation to this repository, +you agree to transfer the copyright of your contribution to The Open University, UK, +and that your contribution will be subject to the above licence. + +## Environment +We use Python 3.10 and the [poetry](https://python-poetry.org) packaging and dependency manager. +To set up the environment: +- if you don't have Python 3.10, [install it](https://www.python.org/downloads/release/python-31011/) +- if you don't have `poetry`, [install it](https://python-poetry.org/docs/#installing-with-the-official-installer) +- clone this repository +- open a terminal and go to the folder to where you cloned this project +- enter `poetry install` + +This installs the software needed to develop `paddles`, in a new +[virtual environment](https://docs.python.org/3/glossary.html#term-virtual-environment), +in order to not interfere with your existing Python projects. + +To use the environment, while developing `paddles`, enter `poetry run C` +to execute command `C` in the virtual environment for `paddles`. + +Alternatively, you can enter `poetry shell` to activate the environment, and +then you can just enter `C` to execute the command. +To deactivate the environment, enter `exit`. + +In the rest of this document, the notation `[poetry run] C` means that you should enter +- `poetry run C` if you haven't activated the environment with `poetry shell` +- `C` if you have. + +To finish the setup, you may optionally enter `[poetry run] pre-commit install` +to install pre-commit hooks (scripts that are run when committing changes to a repository). +Our environment has configured hooks that test, check and format your code and +generate the documentation before you commit your changes to the repository. + +This project folder contains the following files and subfolders: + +- `README.md`: this file +- `LICENSE`: the code licence +- `pyproject.toml`: project configuration +- `poetry.lock`: list of the packages installed by `poetry install` +- `.pre-commit-config.yaml`: list of pre-commit hooks +- `paddles/`: subfolder with the library's code +- `tests/`: subfolder with the test code +- `docs/`: subfolder with the documentation generated from the library code ## Testing -The library is tested with [pytest](https://pytest.org). +We use [pytest](https://pytest.org) to test `paddles`. The unit tests are in the `tests` subfolder, with one test file per library module. There's at least one test function per creator or modifier method. There are no test functions for inspector methods: they are indirectly tested by using them for closed-box (black-box) testing of the modifiers. -Each test file has the same structure: helper functions, -fixtures (functions that generate test data), tests for creator methods, -tests for modifier methods, used separately, and finally tests for combined use of modifiers. +Each test file contains, in this order: +1. helper functions +1. fixtures (functions that generate test data) +1. tests for creator methods +1. tests for modifier methods, used separately, and finally +1. tests for combined use of modifiers. -There are also simple interactive examples of how to use a class in that class's docstring, -in folder `paddles`. These are used as [doctests](https://docs.python.org/3.10/library/doctest.html). +Additionally, there are simple interactive examples of how to use a class in that class's docstring, +in subfolder `paddles`. These are used as [doctests](https://docs.python.org/3.10/library/doctest.html). -To run all tests, the doctests in `paddles` and the unit tests in `tests`, open a terminal, -go the project's main folder, i.e. the parent of `tests`, and enter `poetry run pytest`. -This will produce a report of which tests passed and which failed. -Any library code lines that weren't executed by the tests are also reported. -(We aim for 100% coverage.) +To run all tests, i.e. the doctests in `paddles` and the unit tests in `tests`, +enter `[poetry run] pytest`. +This will produce a report of which tests passed, which failed, and which +library code lines weren't executed. (We aim for 100% coverage.) If two implementations of the same ADT use different messages for the same exception for the same method, the tests fail. -If you installed `pre-commit`, any time you commit one or more Python files, -`pytest` is run before committing. If any test fail, the commit is aborted. -This ensures that any committed code doesn't break the existing tests. -(Coverage below 100% doesn't abort the commit.) - ## Linting -We check and format our code with [ruff](https://astral.sh/ruff). +We check and format all code (library and tests) with [ruff](https://astral.sh/ruff). -Open a terminal in the main project folder and enter `poetry run ruff check`. -The checked style rules are given [here](https://docs.astral.sh/ruff/rules), -with further explanations when you click on a rule's name. +To check the code against over 700 style rules, enter `[poetry run] ruff check`. +If `ruff` reports rule violations, open the [rules page](https://docs.astral.sh/ruff/rules), +search for the reported rule number (e.g. E101), and click on the rule name +(e.g. mixed-spaces-and-tabs) next to it in the page. +This will open a new page explaining the violated rule and its rationale, with an example, +like [this](https://docs.astral.sh/ruff/rules/mixed-spaces-and-tabs/). -To automatically fix violations, if possible, add the command line options -`--fix --unsafe-fixes` and check the changes made by `ruff`. +To automatically fix violations, when possible, +enter `[poetry run] ruff check --fix --unsafe-fixes` and double-check +the modifications made by `ruff`. To automatically ignore the flagged code lines for a particular file, -enter `poetry run ruff check path/to/file.py --add-noqa`. +enter `[poetry run] ruff check path/to/file.py --add-noqa`. This will add comments of the form `# noqa: ...` where `...` is the number of the violated rule. + This should be used sparingly. For example, in the test files, the fixtures that generate classes are on purpose named with initial uppercase, as classes are, which violates the rule that function names and arguments should be in lowercase. -Finally, enter `poetry run ruff format` to format all the code. - -If you installed the pre-commit hooks, the code will be checked and formatted -upon being committed. If one or more style rules are violated, the commit aborts. +Finally, enter `[poetry run] ruff format` to format the code. ## Type checking We type check the code with [pytype](https://google.github.io/pytype). -Enter `poetry run pytype .` to type check all code. -If you installed the pre-commit hooks, type checking will be done upon committing. -If the type checking fails, the commit is aborted. \ No newline at end of file +Enter `[poetry run] pytype .` (note the dot) to type check all code. + +## Documenting +We use [pdoc](https://pdoc.dev) to generate the documentation from the docstrings. + +To check the documents during development, enter `[poetry run] pdoc paddles &` +to open a live site with the documentation. Any changes to the docstrings of +the library files are immediately reflected in the site, upon saving the files. + +## Comitting +If you installed the pre-commit hooks when setting up the [environment](#environment) +then every time you commit your code, +these steps are done automatically on the _staged_ files: +1. test the code with `pytest` +2. type check the code with `pytype` +3. check (but _don't_ fix) the code with `ruff` +4. format the code with `ruff` +5. generate the documentation with `pdoc`. + +If a test or check fails in steps 1–3 or if a file is modified in steps 4–5, +then the commit doesn't go ahead. +This allows you to review the errors and the automatically applied changes, +stage the modified files, and commit again. + +Due to the automated steps, each commit takes many seconds to complete. +But when it successfully completes, you know that your code hasn't broken existing tests, +isn't poorly formatted, and has up-to-date documentation. \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..be5213f --- /dev/null +++ b/docs/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/paddles.html b/docs/paddles.html new file mode 100644 index 0000000..0888d14 --- /dev/null +++ b/docs/paddles.html @@ -0,0 +1,271 @@ + + + + + + + paddles API documentation + + + + + + + + + +
+
+

+paddles

+ +

paddles is a pedagogical algorithms and data structures library.

+ +

It currently implements the Stack, Queue and Deque abstract data types (ADTs).

+ +

The code repository is at https://github.com/dsa-ou/paddles. +The formatted documentation is at https://dsa-ou.github.io/paddles.

+ +

Licence

+ +

paddles is Copyright © 2024 by The Open University, UK. +The code is licensed under a BSD-3-clause licence. +The documentation is licensed under a +Creative Commons Attribution 4.0 International Licence.

+
+ + + + + +
 1"""`paddles` is a pedagogical algorithms and data structures library.
+ 2
+ 3It currently implements the Stack, Queue and Deque abstract data types (ADTs).
+ 4
+ 5The code repository is at https://github.com/dsa-ou/paddles.
+ 6The formatted documentation is at https://dsa-ou.github.io/paddles.
+ 7
+ 8## Licence
+ 9
+10`paddles` is Copyright © 2024 by The Open University, UK.
+11The code is licensed under a [BSD-3-clause licence](https://github.com/dsa-ou/paddles/blob/main/LICENSE).
+12The documentation is licensed under a
+13[Creative Commons Attribution 4.0 International Licence](http://creativecommons.org/licenses/by/4.0).
+14"""
+
+ + +
+
+ + \ No newline at end of file diff --git a/docs/paddles/deque.html b/docs/paddles/deque.html new file mode 100644 index 0000000..83093ce --- /dev/null +++ b/docs/paddles/deque.html @@ -0,0 +1,999 @@ + + + + + + + paddles.deque API documentation + + + + + + + + + +
+
+

+paddles.deque

+ +

This module implements the Deque ADT.

+ +

Intuition

+ +

The Deque ADT models a line of objects that can be accessed, added to and +removed from either end of the line. +A deque can be used as a stack or as a queue.

+ +

Definition

+ +

A deque, pronounced 'deck' and short for 'double-ended queue', is a sequence +where only the members at both ends of the sequence +(called the front and the back of the queue) can be accessed and removed. +New members can only be added at the front or at the back.

+ +

Operations

+ +

The Deque ADT provides operations to:

+ +
    +
  • create a new empty deque
  • +
  • add a new member to the front of the deque
  • +
  • add a new member to the back of the deque
  • +
  • remove the member at the front of the deque
  • +
  • remove the member at the back of the deque
  • +
  • access the member at the front of the deque without removing it
  • +
  • access the member at the back of the deque without removing it
  • +
  • compute the size of the deque (number of members).
  • +
+ +

Applications

+ +

Consider using a deque when you need to simulate a queue where

+ +
    +
  • objects jump the queue (join at the front) or +leave it from the back after waiting a certain time
  • +
  • the direction of the queue changes, like cars on a ferry.
  • +
+ +

Implementations

+ +

The Deque ADT can be implemented with circular dynamic arrays or doubly-linked lists. +In both cases, the operations listed above take constant time. +A doubly-linked list uses much more memory than a static array of the same length, +but a dynamic array may have wasted capacity and requires resizing. +paddles only provides a doubly-linked list implementation for the moment.

+
+ + + + + +
  1"""This module implements the Deque ADT.
+  2
+  3## Intuition
+  4
+  5The Deque ADT models a line of objects that can be accessed, added to and
+  6removed from either end of the line.
+  7A deque can be used as a [stack](stack.html) or as a [queue](queue.html).
+  8
+  9## Definition
+ 10
+ 11A **deque**, pronounced 'deck' and short for 'double-ended queue', is a sequence
+ 12where only the members at both ends of the sequence
+ 13(called the **front** and the **back** of the queue) can be accessed and removed.
+ 14New members can only be added at the front or at the back.
+ 15
+ 16## Operations
+ 17
+ 18The Deque ADT provides operations to:
+ 19- create a new empty deque
+ 20- add a new member to the front of the deque
+ 21- add a new member to the back of the deque
+ 22- remove the member at the front of the deque
+ 23- remove the member at the back of the deque
+ 24- access the member at the front of the deque without removing it
+ 25- access the member at the back of the deque without removing it
+ 26- compute the size of the deque (number of members).
+ 27
+ 28## Applications
+ 29
+ 30Consider using a deque when you need to simulate a queue where
+ 31- objects jump the queue (join at the front) or
+ 32  leave it from the back after waiting a certain time
+ 33- the direction of the queue changes, like cars on a ferry.
+ 34
+ 35## Implementations
+ 36
+ 37The Deque ADT can be implemented with circular dynamic arrays or doubly-linked lists.
+ 38In both cases, the operations listed above take constant time.
+ 39A doubly-linked list uses much more memory than a static array of the same length,
+ 40but a dynamic array may have wasted capacity and requires resizing.
+ 41`paddles` only provides a doubly-linked list implementation for the moment.
+ 42"""
+ 43
+ 44from collections.abc import Sequence
+ 45from typing import Any
+ 46
+ 47__all__ = ["LinkedListDeque"]
+ 48
+ 49# A doubly-linked list node is a list [previous, item, next].
+ 50# These constants make the code more readable.
+ 51PREV = 0
+ 52DATA = 1
+ 53NEXT = 2
+ 54
+ 55
+ 56class LinkedListDeque:
+ 57    """An implementation of the Deque ADT, using a doubly-linked list.
+ 58
+ 59    Besides the ADT's operations, this class provides two convenience operations:
+ 60    - create a non-empty deque from a given sequence
+ 61    - convert a deque to a string, to see its members listed from front to back.
+ 62
+ 63    >>> from paddles.deque import LinkedListDeque
+ 64    >>> deque = LinkedListDeque("abc")          # create a non-empty deque
+ 65    >>> deque.size()                            # number of members
+ 66    3
+ 67    >>> deque.take_front()                      # remove and return the front member
+ 68    'a'
+ 69    >>> deque.take_back()                       # remove and return the back member
+ 70    'c'
+ 71    >>> deque.front() == deque.back() == 'b'    # return the front and back members
+ 72    True
+ 73    >>> deque.add_back("C")                     # add a new member at the back
+ 74    >>> deque.add_front("A")                    # add a new member at the front
+ 75    >>> print(deque)                            # str(deque) also possible
+ 76    LinkedListDeque(['A', 'b', 'C'])
+ 77    """
+ 78
+ 79    def __init__(self, sequence: Sequence[Any] = []) -> None:
+ 80        """Initialize the deque with the members of `sequence`.
+ 81
+ 82        The members are added to the deque in the order they are in `sequence`.
+ 83        To create an empty deque, call `LinkedListDeque()` or `LinkedListDeque([])`.
+ 84
+ 85        Complexity: O(len(`sequence`))
+ 86        """
+ 87        self._head = None
+ 88        self._tail = None
+ 89        self._length = 0
+ 90        if sequence:
+ 91            for item in sequence:
+ 92                self.add_back(item)
+ 93
+ 94    def __str__(self) -> str:
+ 95        """Return a string representation of the deque.
+ 96
+ 97        The string is 'LinkedListDeque([front member, ..., back member])'.
+ 98
+ 99        Complexity: O(self.size())
+100        """
+101        strings = []
+102        current = self._head
+103        while current:
+104            strings.append(repr(current[DATA]))
+105            current = current[NEXT]
+106        return f"LinkedListDeque([{', '.join(strings)}])"
+107
+108    def size(self) -> int:
+109        """Return how many members the deque has.
+110
+111        Complexity: O(1)
+112        """
+113        return self._length
+114
+115    def front(self) -> Any:
+116        """Return the item at the front of the deque, without removing it.
+117
+118        Raise `ValueError` if the deque is empty.
+119
+120        Complexity: O(1)
+121        """
+122        if self.size() == 0:
+123            msg = "can't access the front of an empty deque"
+124            raise ValueError(msg)
+125        return self._head[DATA]
+126
+127    def back(self) -> Any:
+128        """Return the item at the back of the deque, without removing it.
+129
+130        Raise `ValueError` if the deque is empty.
+131
+132        Complexity: O(1)
+133        """
+134        if self.size() == 0:
+135            msg = "can't access the back of an empty deque"
+136            raise ValueError(msg)
+137        return self._tail[DATA]
+138
+139    def add_front(self, item: Any) -> None:
+140        """Put `item` at the front of the deque.
+141
+142        Complexity: O(1)
+143        """
+144        node = [None, item, self._head]
+145        if self.size() == 0:
+146            self._tail = node
+147        else:
+148            self._head[PREV] = node
+149        self._head = node
+150        self._length += 1
+151
+152    def add_back(self, item: Any) -> None:
+153        """Put `item` at the back of the deque.
+154
+155        Complexity: O(1)
+156        """
+157        node = [self._tail, item, None]
+158        if self.size() == 0:
+159            self._head = node
+160        else:
+161            self._tail[NEXT] = node
+162        self._tail = node
+163        self._length += 1
+164
+165    def take_front(self) -> Any:
+166        """Remove and return the item at the front of the deque.
+167
+168        Raise `ValueError` if the deque is empty.
+169
+170        Complexity: O(1)
+171        """
+172        if self.size() == 0:
+173            msg = "can't remove a member from an empty deque"
+174            raise ValueError(msg)
+175        item = self._head[DATA]
+176        self._head = self._head[NEXT]
+177        self._length -= 1
+178        if self.size() == 0:
+179            self._tail = None
+180        else:
+181            self._head[PREV] = None
+182        return item
+183
+184    def take_back(self) -> Any:
+185        """Remove and return the item at the back of the deque.
+186
+187        Raise `ValueError` if the deque is empty.
+188
+189        Complexity: O(1)
+190        """
+191        if self.size() == 0:
+192            msg = "can't remove a member from an empty deque"
+193            raise ValueError(msg)
+194        item = self._tail[DATA]
+195        self._tail = self._tail[PREV]
+196        self._length -= 1
+197        if self.size() == 0:
+198            self._head = None
+199        else:
+200            self._tail[NEXT] = None
+201        return item
+
+ + +
+
+ +
+ + class + LinkedListDeque: + + + +
+ +
 57class LinkedListDeque:
+ 58    """An implementation of the Deque ADT, using a doubly-linked list.
+ 59
+ 60    Besides the ADT's operations, this class provides two convenience operations:
+ 61    - create a non-empty deque from a given sequence
+ 62    - convert a deque to a string, to see its members listed from front to back.
+ 63
+ 64    >>> from paddles.deque import LinkedListDeque
+ 65    >>> deque = LinkedListDeque("abc")          # create a non-empty deque
+ 66    >>> deque.size()                            # number of members
+ 67    3
+ 68    >>> deque.take_front()                      # remove and return the front member
+ 69    'a'
+ 70    >>> deque.take_back()                       # remove and return the back member
+ 71    'c'
+ 72    >>> deque.front() == deque.back() == 'b'    # return the front and back members
+ 73    True
+ 74    >>> deque.add_back("C")                     # add a new member at the back
+ 75    >>> deque.add_front("A")                    # add a new member at the front
+ 76    >>> print(deque)                            # str(deque) also possible
+ 77    LinkedListDeque(['A', 'b', 'C'])
+ 78    """
+ 79
+ 80    def __init__(self, sequence: Sequence[Any] = []) -> None:
+ 81        """Initialize the deque with the members of `sequence`.
+ 82
+ 83        The members are added to the deque in the order they are in `sequence`.
+ 84        To create an empty deque, call `LinkedListDeque()` or `LinkedListDeque([])`.
+ 85
+ 86        Complexity: O(len(`sequence`))
+ 87        """
+ 88        self._head = None
+ 89        self._tail = None
+ 90        self._length = 0
+ 91        if sequence:
+ 92            for item in sequence:
+ 93                self.add_back(item)
+ 94
+ 95    def __str__(self) -> str:
+ 96        """Return a string representation of the deque.
+ 97
+ 98        The string is 'LinkedListDeque([front member, ..., back member])'.
+ 99
+100        Complexity: O(self.size())
+101        """
+102        strings = []
+103        current = self._head
+104        while current:
+105            strings.append(repr(current[DATA]))
+106            current = current[NEXT]
+107        return f"LinkedListDeque([{', '.join(strings)}])"
+108
+109    def size(self) -> int:
+110        """Return how many members the deque has.
+111
+112        Complexity: O(1)
+113        """
+114        return self._length
+115
+116    def front(self) -> Any:
+117        """Return the item at the front of the deque, without removing it.
+118
+119        Raise `ValueError` if the deque is empty.
+120
+121        Complexity: O(1)
+122        """
+123        if self.size() == 0:
+124            msg = "can't access the front of an empty deque"
+125            raise ValueError(msg)
+126        return self._head[DATA]
+127
+128    def back(self) -> Any:
+129        """Return the item at the back of the deque, without removing it.
+130
+131        Raise `ValueError` if the deque is empty.
+132
+133        Complexity: O(1)
+134        """
+135        if self.size() == 0:
+136            msg = "can't access the back of an empty deque"
+137            raise ValueError(msg)
+138        return self._tail[DATA]
+139
+140    def add_front(self, item: Any) -> None:
+141        """Put `item` at the front of the deque.
+142
+143        Complexity: O(1)
+144        """
+145        node = [None, item, self._head]
+146        if self.size() == 0:
+147            self._tail = node
+148        else:
+149            self._head[PREV] = node
+150        self._head = node
+151        self._length += 1
+152
+153    def add_back(self, item: Any) -> None:
+154        """Put `item` at the back of the deque.
+155
+156        Complexity: O(1)
+157        """
+158        node = [self._tail, item, None]
+159        if self.size() == 0:
+160            self._head = node
+161        else:
+162            self._tail[NEXT] = node
+163        self._tail = node
+164        self._length += 1
+165
+166    def take_front(self) -> Any:
+167        """Remove and return the item at the front of the deque.
+168
+169        Raise `ValueError` if the deque is empty.
+170
+171        Complexity: O(1)
+172        """
+173        if self.size() == 0:
+174            msg = "can't remove a member from an empty deque"
+175            raise ValueError(msg)
+176        item = self._head[DATA]
+177        self._head = self._head[NEXT]
+178        self._length -= 1
+179        if self.size() == 0:
+180            self._tail = None
+181        else:
+182            self._head[PREV] = None
+183        return item
+184
+185    def take_back(self) -> Any:
+186        """Remove and return the item at the back of the deque.
+187
+188        Raise `ValueError` if the deque is empty.
+189
+190        Complexity: O(1)
+191        """
+192        if self.size() == 0:
+193            msg = "can't remove a member from an empty deque"
+194            raise ValueError(msg)
+195        item = self._tail[DATA]
+196        self._tail = self._tail[PREV]
+197        self._length -= 1
+198        if self.size() == 0:
+199            self._head = None
+200        else:
+201            self._tail[NEXT] = None
+202        return item
+
+ + +

An implementation of the Deque ADT, using a doubly-linked list.

+ +

Besides the ADT's operations, this class provides two convenience operations:

+ +
    +
  • create a non-empty deque from a given sequence
  • +
  • convert a deque to a string, to see its members listed from front to back.
  • +
+ +
+
>>> from paddles.deque import LinkedListDeque
+>>> deque = LinkedListDeque("abc")          # create a non-empty deque
+>>> deque.size()                            # number of members
+3
+>>> deque.take_front()                      # remove and return the front member
+'a'
+>>> deque.take_back()                       # remove and return the back member
+'c'
+>>> deque.front() == deque.back() == 'b'    # return the front and back members
+True
+>>> deque.add_back("C")                     # add a new member at the back
+>>> deque.add_front("A")                    # add a new member at the front
+>>> print(deque)                            # str(deque) also possible
+LinkedListDeque(['A', 'b', 'C'])
+
+
+
+ + +
+ +
+ + LinkedListDeque(sequence: collections.abc.Sequence[typing.Any] = []) + + + +
+ +
80    def __init__(self, sequence: Sequence[Any] = []) -> None:
+81        """Initialize the deque with the members of `sequence`.
+82
+83        The members are added to the deque in the order they are in `sequence`.
+84        To create an empty deque, call `LinkedListDeque()` or `LinkedListDeque([])`.
+85
+86        Complexity: O(len(`sequence`))
+87        """
+88        self._head = None
+89        self._tail = None
+90        self._length = 0
+91        if sequence:
+92            for item in sequence:
+93                self.add_back(item)
+
+ + +

Initialize the deque with the members of sequence.

+ +

The members are added to the deque in the order they are in sequence. +To create an empty deque, call LinkedListDeque() or LinkedListDeque([]).

+ +

Complexity: O(len(sequence))

+
+ + +
+
+ +
+ + def + size(self) -> int: + + + +
+ +
109    def size(self) -> int:
+110        """Return how many members the deque has.
+111
+112        Complexity: O(1)
+113        """
+114        return self._length
+
+ + +

Return how many members the deque has.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + front(self) -> Any: + + + +
+ +
116    def front(self) -> Any:
+117        """Return the item at the front of the deque, without removing it.
+118
+119        Raise `ValueError` if the deque is empty.
+120
+121        Complexity: O(1)
+122        """
+123        if self.size() == 0:
+124            msg = "can't access the front of an empty deque"
+125            raise ValueError(msg)
+126        return self._head[DATA]
+
+ + +

Return the item at the front of the deque, without removing it.

+ +

Raise ValueError if the deque is empty.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + back(self) -> Any: + + + +
+ +
128    def back(self) -> Any:
+129        """Return the item at the back of the deque, without removing it.
+130
+131        Raise `ValueError` if the deque is empty.
+132
+133        Complexity: O(1)
+134        """
+135        if self.size() == 0:
+136            msg = "can't access the back of an empty deque"
+137            raise ValueError(msg)
+138        return self._tail[DATA]
+
+ + +

Return the item at the back of the deque, without removing it.

+ +

Raise ValueError if the deque is empty.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + add_front(self, item: Any) -> None: + + + +
+ +
140    def add_front(self, item: Any) -> None:
+141        """Put `item` at the front of the deque.
+142
+143        Complexity: O(1)
+144        """
+145        node = [None, item, self._head]
+146        if self.size() == 0:
+147            self._tail = node
+148        else:
+149            self._head[PREV] = node
+150        self._head = node
+151        self._length += 1
+
+ + +

Put item at the front of the deque.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + add_back(self, item: Any) -> None: + + + +
+ +
153    def add_back(self, item: Any) -> None:
+154        """Put `item` at the back of the deque.
+155
+156        Complexity: O(1)
+157        """
+158        node = [self._tail, item, None]
+159        if self.size() == 0:
+160            self._head = node
+161        else:
+162            self._tail[NEXT] = node
+163        self._tail = node
+164        self._length += 1
+
+ + +

Put item at the back of the deque.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + take_front(self) -> Any: + + + +
+ +
166    def take_front(self) -> Any:
+167        """Remove and return the item at the front of the deque.
+168
+169        Raise `ValueError` if the deque is empty.
+170
+171        Complexity: O(1)
+172        """
+173        if self.size() == 0:
+174            msg = "can't remove a member from an empty deque"
+175            raise ValueError(msg)
+176        item = self._head[DATA]
+177        self._head = self._head[NEXT]
+178        self._length -= 1
+179        if self.size() == 0:
+180            self._tail = None
+181        else:
+182            self._head[PREV] = None
+183        return item
+
+ + +

Remove and return the item at the front of the deque.

+ +

Raise ValueError if the deque is empty.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + take_back(self) -> Any: + + + +
+ +
185    def take_back(self) -> Any:
+186        """Remove and return the item at the back of the deque.
+187
+188        Raise `ValueError` if the deque is empty.
+189
+190        Complexity: O(1)
+191        """
+192        if self.size() == 0:
+193            msg = "can't remove a member from an empty deque"
+194            raise ValueError(msg)
+195        item = self._tail[DATA]
+196        self._tail = self._tail[PREV]
+197        self._length -= 1
+198        if self.size() == 0:
+199            self._head = None
+200        else:
+201            self._tail[NEXT] = None
+202        return item
+
+ + +

Remove and return the item at the back of the deque.

+ +

Raise ValueError if the deque is empty.

+ +

Complexity: O(1)

+
+ + +
+
+
+ + \ No newline at end of file diff --git a/docs/paddles/queue.html b/docs/paddles/queue.html new file mode 100644 index 0000000..5a68ffb --- /dev/null +++ b/docs/paddles/queue.html @@ -0,0 +1,785 @@ + + + + + + + paddles.queue API documentation + + + + + + + + + +
+
+

+paddles.queue

+ +

This module implements the Queue ADT.

+ +

Intuition

+ +

The Queue ADT models a line of objects, e.g. cars waiting to board a ferry. +Only the object at the front of the line can be accessed and removed. +The only way to add an object is to put it at the back of the line.

+ +

Definition

+ +

A queue is a sequence where members are added to one end of the sequence +(the back of the queue) and removed from the other end (the front of the queue).

+ +

A queue is a first-in, first-out (FIFO) sequence: +the members are removed in the same order they were added.

+ +

A queue is a sequence ordered by age (time of addition). +The oldest member is at the front of the queue, and the youngest member is at the back.

+ +

Operations

+ +

The Queue ADT provides operations to:

+ +
    +
  • create a new empty queue
  • +
  • add a new member, at the back of the existing ones
  • +
  • remove the member at the front of the queue
  • +
  • access the member at the front of the queue without removing it
  • +
  • compute the size of the queue (number of members).
  • +
+ +

Applications

+ +

Queues are used to implement breadth-first search. +You should consider using a queue when you need to:

+ +
    +
  • simulate a real-life queue, like travellers at passport control or +documents in a printer queue
  • +
  • process items in the same order they were added, like a to-do list.
  • +
+ +

Implementations

+ +

The Queue ADT can be implemented with circular dynamic arrays or singly-linked lists. +In both cases, the operations listed above take constant time. +A singly-linked list uses much more memory than a static array of the same length, +but a dynamic array may have wasted capacity and requires resizing. +paddles only provides a singly-linked list implementation for the moment.

+
+ + + + + +
  1"""This module implements the Queue ADT.
+  2
+  3## Intuition
+  4
+  5The Queue ADT models a line of objects, e.g. cars waiting to board a ferry.
+  6Only the object at the front of the line can be accessed and removed.
+  7The only way to add an object is to put it at the back of the line.
+  8
+  9## Definition
+ 10
+ 11A **queue** is a sequence where members are added to one end of the sequence
+ 12(the **back** of the queue) and removed from the other end (the **front** of the queue).
+ 13
+ 14A queue is a **first-in, first-out (FIFO)** sequence:
+ 15the members are removed in the same order they were added.
+ 16
+ 17A queue is a sequence ordered by age (time of addition).
+ 18The oldest member is at the front of the queue, and the youngest member is at the back.
+ 19
+ 20## Operations
+ 21
+ 22The Queue ADT provides operations to:
+ 23- create a new empty queue
+ 24- add a new member, at the back of the existing ones
+ 25- remove the member at the front of the queue
+ 26- access the member at the front of the queue without removing it
+ 27- compute the size of the queue (number of members).
+ 28
+ 29## Applications
+ 30
+ 31Queues are used to implement breadth-first search.
+ 32You should consider using a queue when you need to:
+ 33- simulate a real-life queue, like travellers at passport control or
+ 34  documents in a printer queue
+ 35- process items in the same order they were added, like a to-do list.
+ 36
+ 37## Implementations
+ 38
+ 39The Queue ADT can be implemented with circular dynamic arrays or singly-linked lists.
+ 40In both cases, the operations listed above take constant time.
+ 41A singly-linked list uses much more memory than a static array of the same length,
+ 42but a dynamic array may have wasted capacity and requires resizing.
+ 43`paddles` only provides a singly-linked list implementation for the moment.
+ 44"""
+ 45
+ 46from collections.abc import Sequence
+ 47from typing import Any
+ 48
+ 49__all__ = ["LinkedListQueue"]
+ 50
+ 51# Each linked list node is a list [item, next].
+ 52# These constants make the code more readable.
+ 53DATA = 0
+ 54NEXT = 1
+ 55
+ 56
+ 57class LinkedListQueue:
+ 58    """An implementation of the Queue ADT, using a singly-linked list.
+ 59
+ 60    Besides the ADT's operations, this class provides two convenience operations:
+ 61    - create a non-empty queue from a given sequence
+ 62    - convert a queue to a string, to see its members listed from front to back.
+ 63
+ 64    >>> from paddles.queue import LinkedListQueue
+ 65    >>> q = LinkedListQueue("abc")  # create a non-empty queue
+ 66    >>> q.size()                    # number of members
+ 67    3
+ 68    >>> q.dequeue()                 # remove and return the front member
+ 69    'a'
+ 70    >>> q.front()                   # return but don't remove the front member
+ 71    'b'
+ 72    >>> q.enqueue("d")              # add a new member at the back
+ 73    >>> print(q)                    # str(q) also possible
+ 74    LinkedListQueue(['b', 'c', 'd'])
+ 75    """
+ 76
+ 77    def __init__(self, sequence: Sequence[Any] = []) -> None:
+ 78        """Initialize the queue with the members of `sequence`.
+ 79
+ 80        The members are added to the queue in the order they are in `sequence`.
+ 81        To create an empty queue, call `LinkedListQueue()` or `LinkedListQueue([])`.
+ 82
+ 83        Complexity: O(len(`sequence`))
+ 84        """
+ 85        self._head = None
+ 86        self._tail = None
+ 87        self._length = 0
+ 88        if sequence:
+ 89            for item in sequence:
+ 90                self.enqueue(item)
+ 91
+ 92    def __str__(self) -> str:
+ 93        """Return a string representation of the queue.
+ 94
+ 95        The string is 'LinkedListQueue([front member, ..., back member])'.
+ 96
+ 97        Complexity: O(self.size())
+ 98        """
+ 99        strings = []
+100        current = self._head
+101        while current:
+102            strings.append(repr(current[DATA]))
+103            current = current[NEXT]
+104        return f"LinkedListQueue([{', '.join(strings)}])"
+105
+106    def size(self) -> int:
+107        """Return how many members the queue has.
+108
+109        Complexity: O(1)
+110        """
+111        return self._length
+112
+113    def front(self) -> Any:
+114        """Return the member at the front of the queue, without removing it.
+115
+116        Raise `ValueError` if the queue is empty.
+117
+118        Complexity: O(1)
+119        """
+120        if self.size() == 0:
+121            msg = "can't access the front of an empty queue"
+122            raise ValueError(msg)
+123        return self._head[DATA]
+124
+125    def enqueue(self, item: Any) -> None:
+126        """Put `item` at the back of the queue.
+127
+128        Complexity: O(1)
+129        """
+130        node = [item, None]
+131        if self.size() == 0:
+132            self._head = node
+133            self._tail = node
+134        else:
+135            self._tail[NEXT] = node
+136            self._tail = node
+137        self._length += 1
+138
+139    def dequeue(self) -> Any:
+140        """Remove and return the item at the front of the queue.
+141
+142        Raise `ValueError` if the queue is empty.
+143
+144        Complexity: O(1)
+145        """
+146        if self.size() == 0:
+147            msg = "can't dequeue from an empty queue"
+148            raise ValueError(msg)
+149        item = self._head[DATA]
+150        self._head = self._head[NEXT]
+151        self._length -= 1
+152        if self.size() == 0:
+153            self._tail = None
+154        return item
+
+ + +
+
+ +
+ + class + LinkedListQueue: + + + +
+ +
 58class LinkedListQueue:
+ 59    """An implementation of the Queue ADT, using a singly-linked list.
+ 60
+ 61    Besides the ADT's operations, this class provides two convenience operations:
+ 62    - create a non-empty queue from a given sequence
+ 63    - convert a queue to a string, to see its members listed from front to back.
+ 64
+ 65    >>> from paddles.queue import LinkedListQueue
+ 66    >>> q = LinkedListQueue("abc")  # create a non-empty queue
+ 67    >>> q.size()                    # number of members
+ 68    3
+ 69    >>> q.dequeue()                 # remove and return the front member
+ 70    'a'
+ 71    >>> q.front()                   # return but don't remove the front member
+ 72    'b'
+ 73    >>> q.enqueue("d")              # add a new member at the back
+ 74    >>> print(q)                    # str(q) also possible
+ 75    LinkedListQueue(['b', 'c', 'd'])
+ 76    """
+ 77
+ 78    def __init__(self, sequence: Sequence[Any] = []) -> None:
+ 79        """Initialize the queue with the members of `sequence`.
+ 80
+ 81        The members are added to the queue in the order they are in `sequence`.
+ 82        To create an empty queue, call `LinkedListQueue()` or `LinkedListQueue([])`.
+ 83
+ 84        Complexity: O(len(`sequence`))
+ 85        """
+ 86        self._head = None
+ 87        self._tail = None
+ 88        self._length = 0
+ 89        if sequence:
+ 90            for item in sequence:
+ 91                self.enqueue(item)
+ 92
+ 93    def __str__(self) -> str:
+ 94        """Return a string representation of the queue.
+ 95
+ 96        The string is 'LinkedListQueue([front member, ..., back member])'.
+ 97
+ 98        Complexity: O(self.size())
+ 99        """
+100        strings = []
+101        current = self._head
+102        while current:
+103            strings.append(repr(current[DATA]))
+104            current = current[NEXT]
+105        return f"LinkedListQueue([{', '.join(strings)}])"
+106
+107    def size(self) -> int:
+108        """Return how many members the queue has.
+109
+110        Complexity: O(1)
+111        """
+112        return self._length
+113
+114    def front(self) -> Any:
+115        """Return the member at the front of the queue, without removing it.
+116
+117        Raise `ValueError` if the queue is empty.
+118
+119        Complexity: O(1)
+120        """
+121        if self.size() == 0:
+122            msg = "can't access the front of an empty queue"
+123            raise ValueError(msg)
+124        return self._head[DATA]
+125
+126    def enqueue(self, item: Any) -> None:
+127        """Put `item` at the back of the queue.
+128
+129        Complexity: O(1)
+130        """
+131        node = [item, None]
+132        if self.size() == 0:
+133            self._head = node
+134            self._tail = node
+135        else:
+136            self._tail[NEXT] = node
+137            self._tail = node
+138        self._length += 1
+139
+140    def dequeue(self) -> Any:
+141        """Remove and return the item at the front of the queue.
+142
+143        Raise `ValueError` if the queue is empty.
+144
+145        Complexity: O(1)
+146        """
+147        if self.size() == 0:
+148            msg = "can't dequeue from an empty queue"
+149            raise ValueError(msg)
+150        item = self._head[DATA]
+151        self._head = self._head[NEXT]
+152        self._length -= 1
+153        if self.size() == 0:
+154            self._tail = None
+155        return item
+
+ + +

An implementation of the Queue ADT, using a singly-linked list.

+ +

Besides the ADT's operations, this class provides two convenience operations:

+ +
    +
  • create a non-empty queue from a given sequence
  • +
  • convert a queue to a string, to see its members listed from front to back.
  • +
+ +
+
>>> from paddles.queue import LinkedListQueue
+>>> q = LinkedListQueue("abc")  # create a non-empty queue
+>>> q.size()                    # number of members
+3
+>>> q.dequeue()                 # remove and return the front member
+'a'
+>>> q.front()                   # return but don't remove the front member
+'b'
+>>> q.enqueue("d")              # add a new member at the back
+>>> print(q)                    # str(q) also possible
+LinkedListQueue(['b', 'c', 'd'])
+
+
+
+ + +
+ +
+ + LinkedListQueue(sequence: collections.abc.Sequence[typing.Any] = []) + + + +
+ +
78    def __init__(self, sequence: Sequence[Any] = []) -> None:
+79        """Initialize the queue with the members of `sequence`.
+80
+81        The members are added to the queue in the order they are in `sequence`.
+82        To create an empty queue, call `LinkedListQueue()` or `LinkedListQueue([])`.
+83
+84        Complexity: O(len(`sequence`))
+85        """
+86        self._head = None
+87        self._tail = None
+88        self._length = 0
+89        if sequence:
+90            for item in sequence:
+91                self.enqueue(item)
+
+ + +

Initialize the queue with the members of sequence.

+ +

The members are added to the queue in the order they are in sequence. +To create an empty queue, call LinkedListQueue() or LinkedListQueue([]).

+ +

Complexity: O(len(sequence))

+
+ + +
+
+ +
+ + def + size(self) -> int: + + + +
+ +
107    def size(self) -> int:
+108        """Return how many members the queue has.
+109
+110        Complexity: O(1)
+111        """
+112        return self._length
+
+ + +

Return how many members the queue has.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + front(self) -> Any: + + + +
+ +
114    def front(self) -> Any:
+115        """Return the member at the front of the queue, without removing it.
+116
+117        Raise `ValueError` if the queue is empty.
+118
+119        Complexity: O(1)
+120        """
+121        if self.size() == 0:
+122            msg = "can't access the front of an empty queue"
+123            raise ValueError(msg)
+124        return self._head[DATA]
+
+ + +

Return the member at the front of the queue, without removing it.

+ +

Raise ValueError if the queue is empty.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + enqueue(self, item: Any) -> None: + + + +
+ +
126    def enqueue(self, item: Any) -> None:
+127        """Put `item` at the back of the queue.
+128
+129        Complexity: O(1)
+130        """
+131        node = [item, None]
+132        if self.size() == 0:
+133            self._head = node
+134            self._tail = node
+135        else:
+136            self._tail[NEXT] = node
+137            self._tail = node
+138        self._length += 1
+
+ + +

Put item at the back of the queue.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + dequeue(self) -> Any: + + + +
+ +
140    def dequeue(self) -> Any:
+141        """Remove and return the item at the front of the queue.
+142
+143        Raise `ValueError` if the queue is empty.
+144
+145        Complexity: O(1)
+146        """
+147        if self.size() == 0:
+148            msg = "can't dequeue from an empty queue"
+149            raise ValueError(msg)
+150        item = self._head[DATA]
+151        self._head = self._head[NEXT]
+152        self._length -= 1
+153        if self.size() == 0:
+154            self._tail = None
+155        return item
+
+ + +

Remove and return the item at the front of the queue.

+ +

Raise ValueError if the queue is empty.

+ +

Complexity: O(1)

+
+ + +
+
+
+ + \ No newline at end of file diff --git a/docs/paddles/stack.html b/docs/paddles/stack.html new file mode 100644 index 0000000..3bbeab3 --- /dev/null +++ b/docs/paddles/stack.html @@ -0,0 +1,1134 @@ + + + + + + + paddles.stack API documentation + + + + + + + + + +
+
+

+paddles.stack

+ +

This module implements the Stack ADT.

+ +

Intuition

+ +

The Stack ADT models a pile of objects, e.g. a pile of storage boxes. +Only the object at the top of the pile can be accessed and removed. +The only way to add an object is to put it on top of the existing pile.

+ +

Definition

+ +

A stack is a sequence where members are removed from and added to +the same end of the sequence, called the top of the stack.

+ +

A stack is a last-in, first-out (LIFO) sequence: +the next member to be removed is the one most recently added.

+ +

A stack is a sequence ordered by age (time of addition). +The oldest member is at the bottom of the stack, and the youngest member is at the top.

+ +

Operations

+ +

The Stack ADT provides operations to:

+ +
    +
  • create a new empty stack
  • +
  • add a new member, on top of the existing ones
  • +
  • remove the member at the top of the stack
  • +
  • access the member at the top of the stack without removing it
  • +
  • compute the size of the stack (number of members).
  • +
+ +

Applications

+ +

Stacks are used to implement function calls and depth-first search. +You should consider using a stack when you need to:

+ +
    +
  • simulate the handling of a pile of objects, like loading and unloading ship containers
  • +
  • process nested structures, like brackets (e.g. print([1, {2, 3}])) or +HTML tags (e.g. <p><b>text</b></p>)
  • +
  • process items in the reverse order they were added, like undo operations +(the next command to be undone is the most recently executed one).
  • +
+ +

Implementations

+ +

The Stack ADT can be implemented with dynamic arrays or singly-linked lists. +In both cases, the operations listed above take constant time. +A singly-linked list uses much more memory than a static array of the same length, +but a dynamic array may have wasted capacity and requires resizing.

+
+ + + + + +
  1"""This module implements the Stack ADT.
+  2
+  3## Intuition
+  4
+  5The Stack ADT models a pile of objects, e.g. a pile of storage boxes.
+  6Only the object at the top of the pile can be accessed and removed.
+  7The only way to add an object is to put it on top of the existing pile.
+  8
+  9## Definition
+ 10
+ 11A **stack** is a sequence where members are removed from and added to
+ 12the same end of the sequence, called the **top** of the stack.
+ 13
+ 14A stack is a **last-in, first-out (LIFO)** sequence:
+ 15the next member to be removed is the one most recently added.
+ 16
+ 17A stack is a sequence ordered by age (time of addition).
+ 18The oldest member is at the bottom of the stack, and the youngest member is at the top.
+ 19
+ 20## Operations
+ 21
+ 22The Stack ADT provides operations to:
+ 23- create a new empty stack
+ 24- add a new member, on top of the existing ones
+ 25- remove the member at the top of the stack
+ 26- access the member at the top of the stack without removing it
+ 27- compute the size of the stack (number of members).
+ 28
+ 29## Applications
+ 30
+ 31Stacks are used to implement function calls and depth-first search.
+ 32You should consider using a stack when you need to:
+ 33- simulate the handling of a pile of objects, like loading and unloading ship containers
+ 34- process nested structures, like brackets (e.g. `print([1, {2, 3}])`) or
+ 35  HTML tags (e.g. `<p><b>text</b></p>`)
+ 36- process items in the reverse order they were added, like undo operations
+ 37  (the next command to be undone is the most recently executed one).
+ 38
+ 39## Implementations
+ 40
+ 41The Stack ADT can be implemented with dynamic arrays or singly-linked lists.
+ 42In both cases, the operations listed above take constant time.
+ 43A singly-linked list uses much more memory than a static array of the same length,
+ 44but a dynamic array may have wasted capacity and requires resizing.
+ 45"""
+ 46
+ 47from collections.abc import Sequence
+ 48from typing import Any
+ 49
+ 50__all__ = ["DynamicArrayStack", "LinkedListStack"]
+ 51
+ 52
+ 53class DynamicArrayStack:
+ 54    """An implementation of the Stack ADT, using Python lists.
+ 55
+ 56    Besides the ADT's operations, this class provides two convenience operations:
+ 57    - create a non-empty stack from a given sequence
+ 58    - convert a stack to a string, to see its members listed from bottom to top.
+ 59
+ 60    >>> from paddles.stack import DynamicArrayStack
+ 61    >>> stack = DynamicArrayStack("abc")    # create a non-empty stack
+ 62    >>> stack.size()                        # number of members
+ 63    3
+ 64    >>> stack.pop()                         # remove and return the top member
+ 65    'c'
+ 66    >>> stack.peek()                        # return but don't remove the top member
+ 67    'b'
+ 68    >>> stack.push("C")                     # add a new member on top
+ 69    >>> print(stack)                        # str(stack) also possible
+ 70    DynamicArrayStack(['a', 'b', 'C'])
+ 71    """
+ 72
+ 73    def __init__(self, sequence: Sequence[Any] = []) -> None:
+ 74        """Initialize the stack with the members of `sequence`.
+ 75
+ 76        The members are added to the stack in the order they are in `sequence`.
+ 77        To create an empty stack, call `DynamicArrayStack()` or `DynamicArrayStack([])`.
+ 78
+ 79        Complexity: O(len(`sequence`))
+ 80        """
+ 81        self._members = []
+ 82        for item in sequence:
+ 83            self.push(item)
+ 84
+ 85    def __str__(self) -> str:
+ 86        """Return a string representation of the stack.
+ 87
+ 88        The string is 'DynamicArrayStack([bottom member, ..., top member])'.
+ 89
+ 90        Complexity: O(self.size())
+ 91        """
+ 92        return f"DynamicArrayStack({self._members})"
+ 93
+ 94    def size(self) -> int:
+ 95        """Return how many members the stack has.
+ 96
+ 97        Complexity: O(1)
+ 98        """
+ 99        return len(self._members)
+100
+101    def peek(self) -> Any:
+102        """Return the member at the top of the stack, without removing it.
+103
+104        Raise `ValueError` if the stack is empty.
+105
+106        Complexity: O(1)
+107        """
+108        if self.size() == 0:
+109            msg = "can't peek into an empty stack"
+110            raise ValueError(msg)
+111        return self._members[-1]
+112
+113    def push(self, item: Any) -> None:
+114        """Put `item` on top of the stack.
+115
+116        Complexity: O(1)
+117        """
+118        self._members.append(item)
+119
+120    def pop(self) -> Any:
+121        """Remove and return the member at the top of the stack.
+122
+123        Raise `ValueError` if the stack is empty.
+124
+125        Complexity: O(1)
+126        """
+127        if self.size() == 0:
+128            msg = "can't pop a member from an empty stack"
+129            raise ValueError(msg)
+130        return self._members.pop()
+131
+132
+133# Each linked list node is a tuple (data, next).
+134# These constants make the code more readable.
+135DATA = 0
+136NEXT = 1
+137
+138
+139class LinkedListStack:
+140    """An implementation of the Stack ADT, using singly-linked lists.
+141
+142    Besides the ADT's operations, this class provides two convenience operations:
+143    - create a non-empty stack from a given sequence
+144    - convert a stack to a string, to see its members listed from bottom to top.
+145
+146    >>> from paddles.stack import LinkedListStack
+147    >>> stack = LinkedListStack("abc")      # create a non-empty stack
+148    >>> stack.size()                        # number of members
+149    3
+150    >>> stack.pop()                         # remove and return the top member
+151    'c'
+152    >>> stack.peek()                        # return but don't remove the top member
+153    'b'
+154    >>> stack.push("C")                     # add a new member on top
+155    >>> print(stack)                        # str(stack) also possible
+156    LinkedListStack(['a', 'b', 'C'])
+157    """
+158
+159    def __init__(self, sequence: Sequence[Any] = []) -> None:
+160        """Initialize the stack with the members of `sequence`.
+161
+162        The members are added to the stack in the order they are in `sequence`.
+163        To create an empty stack, call `LinkedListStack()` or `LinkedListStack([])`.
+164
+165        Complexity: O(len(`sequence`))
+166        """
+167        self._head = None
+168        self._length = 0
+169        for item in sequence:
+170            self.push(item)
+171
+172    def __str__(self) -> str:
+173        """Return a string representation of the stack.
+174
+175        The string is 'LinkedListStack([bottom member, ..., top member])'.
+176
+177        Complexity: O(self.size())
+178        """
+179        strings = []
+180        current = self._head
+181        while current:
+182            strings.append(repr(current[DATA]))
+183            current = current[NEXT]
+184        return "LinkedListStack([" + ", ".join(reversed(strings)) + "])"
+185
+186    def size(self) -> int:
+187        """Return how many members the stack has.
+188
+189        Complexity: O(1)
+190        """
+191        return self._length
+192
+193    def push(self, item: Any) -> None:
+194        """Put `item` on top of the stack.
+195
+196        Complexity: O(1)
+197        """
+198        self._head = (item, self._head)
+199        self._length += 1
+200
+201    def pop(self) -> Any:
+202        """Remove and return the member at the top of the stack.
+203
+204        Raise `ValueError` if the stack is empty.
+205
+206        Complexity: O(1)
+207        """
+208        if self.size() == 0:
+209            msg = "can't pop a member from an empty stack"
+210            raise ValueError(msg)
+211        item = self._head[DATA]
+212        self._head = self._head[NEXT]
+213        self._length -= 1
+214        return item
+215
+216    def peek(self) -> Any:
+217        """Return the member at the top of the stack.
+218
+219        Raise `ValueError` if the stack is empty.
+220
+221        Complexity: O(1)
+222        """
+223        if self.size() == 0:
+224            msg = "can't peek into an empty stack"
+225            raise ValueError(msg)
+226        return self._head[DATA]
+
+ + +
+
+ +
+ + class + DynamicArrayStack: + + + +
+ +
 54class DynamicArrayStack:
+ 55    """An implementation of the Stack ADT, using Python lists.
+ 56
+ 57    Besides the ADT's operations, this class provides two convenience operations:
+ 58    - create a non-empty stack from a given sequence
+ 59    - convert a stack to a string, to see its members listed from bottom to top.
+ 60
+ 61    >>> from paddles.stack import DynamicArrayStack
+ 62    >>> stack = DynamicArrayStack("abc")    # create a non-empty stack
+ 63    >>> stack.size()                        # number of members
+ 64    3
+ 65    >>> stack.pop()                         # remove and return the top member
+ 66    'c'
+ 67    >>> stack.peek()                        # return but don't remove the top member
+ 68    'b'
+ 69    >>> stack.push("C")                     # add a new member on top
+ 70    >>> print(stack)                        # str(stack) also possible
+ 71    DynamicArrayStack(['a', 'b', 'C'])
+ 72    """
+ 73
+ 74    def __init__(self, sequence: Sequence[Any] = []) -> None:
+ 75        """Initialize the stack with the members of `sequence`.
+ 76
+ 77        The members are added to the stack in the order they are in `sequence`.
+ 78        To create an empty stack, call `DynamicArrayStack()` or `DynamicArrayStack([])`.
+ 79
+ 80        Complexity: O(len(`sequence`))
+ 81        """
+ 82        self._members = []
+ 83        for item in sequence:
+ 84            self.push(item)
+ 85
+ 86    def __str__(self) -> str:
+ 87        """Return a string representation of the stack.
+ 88
+ 89        The string is 'DynamicArrayStack([bottom member, ..., top member])'.
+ 90
+ 91        Complexity: O(self.size())
+ 92        """
+ 93        return f"DynamicArrayStack({self._members})"
+ 94
+ 95    def size(self) -> int:
+ 96        """Return how many members the stack has.
+ 97
+ 98        Complexity: O(1)
+ 99        """
+100        return len(self._members)
+101
+102    def peek(self) -> Any:
+103        """Return the member at the top of the stack, without removing it.
+104
+105        Raise `ValueError` if the stack is empty.
+106
+107        Complexity: O(1)
+108        """
+109        if self.size() == 0:
+110            msg = "can't peek into an empty stack"
+111            raise ValueError(msg)
+112        return self._members[-1]
+113
+114    def push(self, item: Any) -> None:
+115        """Put `item` on top of the stack.
+116
+117        Complexity: O(1)
+118        """
+119        self._members.append(item)
+120
+121    def pop(self) -> Any:
+122        """Remove and return the member at the top of the stack.
+123
+124        Raise `ValueError` if the stack is empty.
+125
+126        Complexity: O(1)
+127        """
+128        if self.size() == 0:
+129            msg = "can't pop a member from an empty stack"
+130            raise ValueError(msg)
+131        return self._members.pop()
+
+ + +

An implementation of the Stack ADT, using Python lists.

+ +

Besides the ADT's operations, this class provides two convenience operations:

+ +
    +
  • create a non-empty stack from a given sequence
  • +
  • convert a stack to a string, to see its members listed from bottom to top.
  • +
+ +
+
>>> from paddles.stack import DynamicArrayStack
+>>> stack = DynamicArrayStack("abc")    # create a non-empty stack
+>>> stack.size()                        # number of members
+3
+>>> stack.pop()                         # remove and return the top member
+'c'
+>>> stack.peek()                        # return but don't remove the top member
+'b'
+>>> stack.push("C")                     # add a new member on top
+>>> print(stack)                        # str(stack) also possible
+DynamicArrayStack(['a', 'b', 'C'])
+
+
+
+ + +
+ +
+ + DynamicArrayStack(sequence: collections.abc.Sequence[typing.Any] = []) + + + +
+ +
74    def __init__(self, sequence: Sequence[Any] = []) -> None:
+75        """Initialize the stack with the members of `sequence`.
+76
+77        The members are added to the stack in the order they are in `sequence`.
+78        To create an empty stack, call `DynamicArrayStack()` or `DynamicArrayStack([])`.
+79
+80        Complexity: O(len(`sequence`))
+81        """
+82        self._members = []
+83        for item in sequence:
+84            self.push(item)
+
+ + +

Initialize the stack with the members of sequence.

+ +

The members are added to the stack in the order they are in sequence. +To create an empty stack, call DynamicArrayStack() or DynamicArrayStack([]).

+ +

Complexity: O(len(sequence))

+
+ + +
+
+ +
+ + def + size(self) -> int: + + + +
+ +
 95    def size(self) -> int:
+ 96        """Return how many members the stack has.
+ 97
+ 98        Complexity: O(1)
+ 99        """
+100        return len(self._members)
+
+ + +

Return how many members the stack has.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + peek(self) -> Any: + + + +
+ +
102    def peek(self) -> Any:
+103        """Return the member at the top of the stack, without removing it.
+104
+105        Raise `ValueError` if the stack is empty.
+106
+107        Complexity: O(1)
+108        """
+109        if self.size() == 0:
+110            msg = "can't peek into an empty stack"
+111            raise ValueError(msg)
+112        return self._members[-1]
+
+ + +

Return the member at the top of the stack, without removing it.

+ +

Raise ValueError if the stack is empty.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + push(self, item: Any) -> None: + + + +
+ +
114    def push(self, item: Any) -> None:
+115        """Put `item` on top of the stack.
+116
+117        Complexity: O(1)
+118        """
+119        self._members.append(item)
+
+ + +

Put item on top of the stack.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + pop(self) -> Any: + + + +
+ +
121    def pop(self) -> Any:
+122        """Remove and return the member at the top of the stack.
+123
+124        Raise `ValueError` if the stack is empty.
+125
+126        Complexity: O(1)
+127        """
+128        if self.size() == 0:
+129            msg = "can't pop a member from an empty stack"
+130            raise ValueError(msg)
+131        return self._members.pop()
+
+ + +

Remove and return the member at the top of the stack.

+ +

Raise ValueError if the stack is empty.

+ +

Complexity: O(1)

+
+ + +
+
+
+ +
+ + class + LinkedListStack: + + + +
+ +
140class LinkedListStack:
+141    """An implementation of the Stack ADT, using singly-linked lists.
+142
+143    Besides the ADT's operations, this class provides two convenience operations:
+144    - create a non-empty stack from a given sequence
+145    - convert a stack to a string, to see its members listed from bottom to top.
+146
+147    >>> from paddles.stack import LinkedListStack
+148    >>> stack = LinkedListStack("abc")      # create a non-empty stack
+149    >>> stack.size()                        # number of members
+150    3
+151    >>> stack.pop()                         # remove and return the top member
+152    'c'
+153    >>> stack.peek()                        # return but don't remove the top member
+154    'b'
+155    >>> stack.push("C")                     # add a new member on top
+156    >>> print(stack)                        # str(stack) also possible
+157    LinkedListStack(['a', 'b', 'C'])
+158    """
+159
+160    def __init__(self, sequence: Sequence[Any] = []) -> None:
+161        """Initialize the stack with the members of `sequence`.
+162
+163        The members are added to the stack in the order they are in `sequence`.
+164        To create an empty stack, call `LinkedListStack()` or `LinkedListStack([])`.
+165
+166        Complexity: O(len(`sequence`))
+167        """
+168        self._head = None
+169        self._length = 0
+170        for item in sequence:
+171            self.push(item)
+172
+173    def __str__(self) -> str:
+174        """Return a string representation of the stack.
+175
+176        The string is 'LinkedListStack([bottom member, ..., top member])'.
+177
+178        Complexity: O(self.size())
+179        """
+180        strings = []
+181        current = self._head
+182        while current:
+183            strings.append(repr(current[DATA]))
+184            current = current[NEXT]
+185        return "LinkedListStack([" + ", ".join(reversed(strings)) + "])"
+186
+187    def size(self) -> int:
+188        """Return how many members the stack has.
+189
+190        Complexity: O(1)
+191        """
+192        return self._length
+193
+194    def push(self, item: Any) -> None:
+195        """Put `item` on top of the stack.
+196
+197        Complexity: O(1)
+198        """
+199        self._head = (item, self._head)
+200        self._length += 1
+201
+202    def pop(self) -> Any:
+203        """Remove and return the member at the top of the stack.
+204
+205        Raise `ValueError` if the stack is empty.
+206
+207        Complexity: O(1)
+208        """
+209        if self.size() == 0:
+210            msg = "can't pop a member from an empty stack"
+211            raise ValueError(msg)
+212        item = self._head[DATA]
+213        self._head = self._head[NEXT]
+214        self._length -= 1
+215        return item
+216
+217    def peek(self) -> Any:
+218        """Return the member at the top of the stack.
+219
+220        Raise `ValueError` if the stack is empty.
+221
+222        Complexity: O(1)
+223        """
+224        if self.size() == 0:
+225            msg = "can't peek into an empty stack"
+226            raise ValueError(msg)
+227        return self._head[DATA]
+
+ + +

An implementation of the Stack ADT, using singly-linked lists.

+ +

Besides the ADT's operations, this class provides two convenience operations:

+ +
    +
  • create a non-empty stack from a given sequence
  • +
  • convert a stack to a string, to see its members listed from bottom to top.
  • +
+ +
+
>>> from paddles.stack import LinkedListStack
+>>> stack = LinkedListStack("abc")      # create a non-empty stack
+>>> stack.size()                        # number of members
+3
+>>> stack.pop()                         # remove and return the top member
+'c'
+>>> stack.peek()                        # return but don't remove the top member
+'b'
+>>> stack.push("C")                     # add a new member on top
+>>> print(stack)                        # str(stack) also possible
+LinkedListStack(['a', 'b', 'C'])
+
+
+
+ + +
+ +
+ + LinkedListStack(sequence: collections.abc.Sequence[typing.Any] = []) + + + +
+ +
160    def __init__(self, sequence: Sequence[Any] = []) -> None:
+161        """Initialize the stack with the members of `sequence`.
+162
+163        The members are added to the stack in the order they are in `sequence`.
+164        To create an empty stack, call `LinkedListStack()` or `LinkedListStack([])`.
+165
+166        Complexity: O(len(`sequence`))
+167        """
+168        self._head = None
+169        self._length = 0
+170        for item in sequence:
+171            self.push(item)
+
+ + +

Initialize the stack with the members of sequence.

+ +

The members are added to the stack in the order they are in sequence. +To create an empty stack, call LinkedListStack() or LinkedListStack([]).

+ +

Complexity: O(len(sequence))

+
+ + +
+
+ +
+ + def + size(self) -> int: + + + +
+ +
187    def size(self) -> int:
+188        """Return how many members the stack has.
+189
+190        Complexity: O(1)
+191        """
+192        return self._length
+
+ + +

Return how many members the stack has.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + push(self, item: Any) -> None: + + + +
+ +
194    def push(self, item: Any) -> None:
+195        """Put `item` on top of the stack.
+196
+197        Complexity: O(1)
+198        """
+199        self._head = (item, self._head)
+200        self._length += 1
+
+ + +

Put item on top of the stack.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + pop(self) -> Any: + + + +
+ +
202    def pop(self) -> Any:
+203        """Remove and return the member at the top of the stack.
+204
+205        Raise `ValueError` if the stack is empty.
+206
+207        Complexity: O(1)
+208        """
+209        if self.size() == 0:
+210            msg = "can't pop a member from an empty stack"
+211            raise ValueError(msg)
+212        item = self._head[DATA]
+213        self._head = self._head[NEXT]
+214        self._length -= 1
+215        return item
+
+ + +

Remove and return the member at the top of the stack.

+ +

Raise ValueError if the stack is empty.

+ +

Complexity: O(1)

+
+ + +
+
+ +
+ + def + peek(self) -> Any: + + + +
+ +
217    def peek(self) -> Any:
+218        """Return the member at the top of the stack.
+219
+220        Raise `ValueError` if the stack is empty.
+221
+222        Complexity: O(1)
+223        """
+224        if self.size() == 0:
+225            msg = "can't peek into an empty stack"
+226            raise ValueError(msg)
+227        return self._head[DATA]
+
+ + +

Return the member at the top of the stack.

+ +

Raise ValueError if the stack is empty.

+ +

Complexity: O(1)

+
+ + +
+
+
+ + \ No newline at end of file diff --git a/docs/search.js b/docs/search.js new file mode 100644 index 0000000..8b6eb2c --- /dev/null +++ b/docs/search.js @@ -0,0 +1,46 @@ +window.pdocSearch = (function(){ +/** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();opaddles is a pedagogical algorithms and data structures library.

\n\n

It currently implements the Stack, Queue and Deque abstract data types (ADTs).

\n\n

The code repository is at https://github.com/dsa-ou/paddles.\nThe formatted documentation is at https://dsa-ou.github.io/paddles.

\n\n

Licence

\n\n

paddles is Copyright \u00a9 2024 by The Open University, UK.\nThe code is licensed under a BSD-3-clause licence.\nThe documentation is licensed under a\nCreative Commons Attribution 4.0 International Licence.

\n"}, {"fullname": "paddles.deque", "modulename": "paddles.deque", "kind": "module", "doc": "

This module implements the Deque ADT.

\n\n

Intuition

\n\n

The Deque ADT models a line of objects that can be accessed, added to and\nremoved from either end of the line.\nA deque can be used as a stack or as a queue.

\n\n

Definition

\n\n

A deque, pronounced 'deck' and short for 'double-ended queue', is a sequence\nwhere only the members at both ends of the sequence\n(called the front and the back of the queue) can be accessed and removed.\nNew members can only be added at the front or at the back.

\n\n

Operations

\n\n

The Deque ADT provides operations to:

\n\n
    \n
  • create a new empty deque
  • \n
  • add a new member to the front of the deque
  • \n
  • add a new member to the back of the deque
  • \n
  • remove the member at the front of the deque
  • \n
  • remove the member at the back of the deque
  • \n
  • access the member at the front of the deque without removing it
  • \n
  • access the member at the back of the deque without removing it
  • \n
  • compute the size of the deque (number of members).
  • \n
\n\n

Applications

\n\n

Consider using a deque when you need to simulate a queue where

\n\n
    \n
  • objects jump the queue (join at the front) or\nleave it from the back after waiting a certain time
  • \n
  • the direction of the queue changes, like cars on a ferry.
  • \n
\n\n

Implementations

\n\n

The Deque ADT can be implemented with circular dynamic arrays or doubly-linked lists.\nIn both cases, the operations listed above take constant time.\nA doubly-linked list uses much more memory than a static array of the same length,\nbut a dynamic array may have wasted capacity and requires resizing.\npaddles only provides a doubly-linked list implementation for the moment.

\n"}, {"fullname": "paddles.deque.LinkedListDeque", "modulename": "paddles.deque", "qualname": "LinkedListDeque", "kind": "class", "doc": "

An implementation of the Deque ADT, using a doubly-linked list.

\n\n

Besides the ADT's operations, this class provides two convenience operations:

\n\n
    \n
  • create a non-empty deque from a given sequence
  • \n
  • convert a deque to a string, to see its members listed from front to back.
  • \n
\n\n
\n
>>> from paddles.deque import LinkedListDeque\n>>> deque = LinkedListDeque("abc")          # create a non-empty deque\n>>> deque.size()                            # number of members\n3\n>>> deque.take_front()                      # remove and return the front member\n'a'\n>>> deque.take_back()                       # remove and return the back member\n'c'\n>>> deque.front() == deque.back() == 'b'    # return the front and back members\nTrue\n>>> deque.add_back("C")                     # add a new member at the back\n>>> deque.add_front("A")                    # add a new member at the front\n>>> print(deque)                            # str(deque) also possible\nLinkedListDeque(['A', 'b', 'C'])\n
\n
\n"}, {"fullname": "paddles.deque.LinkedListDeque.__init__", "modulename": "paddles.deque", "qualname": "LinkedListDeque.__init__", "kind": "function", "doc": "

Initialize the deque with the members of sequence.

\n\n

The members are added to the deque in the order they are in sequence.\nTo create an empty deque, call LinkedListDeque() or LinkedListDeque([]).

\n\n

Complexity: O(len(sequence))

\n", "signature": "(sequence: collections.abc.Sequence[typing.Any] = [])"}, {"fullname": "paddles.deque.LinkedListDeque.size", "modulename": "paddles.deque", "qualname": "LinkedListDeque.size", "kind": "function", "doc": "

Return how many members the deque has.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "paddles.deque.LinkedListDeque.front", "modulename": "paddles.deque", "qualname": "LinkedListDeque.front", "kind": "function", "doc": "

Return the item at the front of the deque, without removing it.

\n\n

Raise ValueError if the deque is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.deque.LinkedListDeque.back", "modulename": "paddles.deque", "qualname": "LinkedListDeque.back", "kind": "function", "doc": "

Return the item at the back of the deque, without removing it.

\n\n

Raise ValueError if the deque is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.deque.LinkedListDeque.add_front", "modulename": "paddles.deque", "qualname": "LinkedListDeque.add_front", "kind": "function", "doc": "

Put item at the front of the deque.

\n\n

Complexity: O(1)

\n", "signature": "(self, item: Any) -> None:", "funcdef": "def"}, {"fullname": "paddles.deque.LinkedListDeque.add_back", "modulename": "paddles.deque", "qualname": "LinkedListDeque.add_back", "kind": "function", "doc": "

Put item at the back of the deque.

\n\n

Complexity: O(1)

\n", "signature": "(self, item: Any) -> None:", "funcdef": "def"}, {"fullname": "paddles.deque.LinkedListDeque.take_front", "modulename": "paddles.deque", "qualname": "LinkedListDeque.take_front", "kind": "function", "doc": "

Remove and return the item at the front of the deque.

\n\n

Raise ValueError if the deque is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.deque.LinkedListDeque.take_back", "modulename": "paddles.deque", "qualname": "LinkedListDeque.take_back", "kind": "function", "doc": "

Remove and return the item at the back of the deque.

\n\n

Raise ValueError if the deque is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.queue", "modulename": "paddles.queue", "kind": "module", "doc": "

This module implements the Queue ADT.

\n\n

Intuition

\n\n

The Queue ADT models a line of objects, e.g. cars waiting to board a ferry.\nOnly the object at the front of the line can be accessed and removed.\nThe only way to add an object is to put it at the back of the line.

\n\n

Definition

\n\n

A queue is a sequence where members are added to one end of the sequence\n(the back of the queue) and removed from the other end (the front of the queue).

\n\n

A queue is a first-in, first-out (FIFO) sequence:\nthe members are removed in the same order they were added.

\n\n

A queue is a sequence ordered by age (time of addition).\nThe oldest member is at the front of the queue, and the youngest member is at the back.

\n\n

Operations

\n\n

The Queue ADT provides operations to:

\n\n
    \n
  • create a new empty queue
  • \n
  • add a new member, at the back of the existing ones
  • \n
  • remove the member at the front of the queue
  • \n
  • access the member at the front of the queue without removing it
  • \n
  • compute the size of the queue (number of members).
  • \n
\n\n

Applications

\n\n

Queues are used to implement breadth-first search.\nYou should consider using a queue when you need to:

\n\n
    \n
  • simulate a real-life queue, like travellers at passport control or\ndocuments in a printer queue
  • \n
  • process items in the same order they were added, like a to-do list.
  • \n
\n\n

Implementations

\n\n

The Queue ADT can be implemented with circular dynamic arrays or singly-linked lists.\nIn both cases, the operations listed above take constant time.\nA singly-linked list uses much more memory than a static array of the same length,\nbut a dynamic array may have wasted capacity and requires resizing.\npaddles only provides a singly-linked list implementation for the moment.

\n"}, {"fullname": "paddles.queue.LinkedListQueue", "modulename": "paddles.queue", "qualname": "LinkedListQueue", "kind": "class", "doc": "

An implementation of the Queue ADT, using a singly-linked list.

\n\n

Besides the ADT's operations, this class provides two convenience operations:

\n\n
    \n
  • create a non-empty queue from a given sequence
  • \n
  • convert a queue to a string, to see its members listed from front to back.
  • \n
\n\n
\n
>>> from paddles.queue import LinkedListQueue\n>>> q = LinkedListQueue("abc")  # create a non-empty queue\n>>> q.size()                    # number of members\n3\n>>> q.dequeue()                 # remove and return the front member\n'a'\n>>> q.front()                   # return but don't remove the front member\n'b'\n>>> q.enqueue("d")              # add a new member at the back\n>>> print(q)                    # str(q) also possible\nLinkedListQueue(['b', 'c', 'd'])\n
\n
\n"}, {"fullname": "paddles.queue.LinkedListQueue.__init__", "modulename": "paddles.queue", "qualname": "LinkedListQueue.__init__", "kind": "function", "doc": "

Initialize the queue with the members of sequence.

\n\n

The members are added to the queue in the order they are in sequence.\nTo create an empty queue, call LinkedListQueue() or LinkedListQueue([]).

\n\n

Complexity: O(len(sequence))

\n", "signature": "(sequence: collections.abc.Sequence[typing.Any] = [])"}, {"fullname": "paddles.queue.LinkedListQueue.size", "modulename": "paddles.queue", "qualname": "LinkedListQueue.size", "kind": "function", "doc": "

Return how many members the queue has.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "paddles.queue.LinkedListQueue.front", "modulename": "paddles.queue", "qualname": "LinkedListQueue.front", "kind": "function", "doc": "

Return the member at the front of the queue, without removing it.

\n\n

Raise ValueError if the queue is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.queue.LinkedListQueue.enqueue", "modulename": "paddles.queue", "qualname": "LinkedListQueue.enqueue", "kind": "function", "doc": "

Put item at the back of the queue.

\n\n

Complexity: O(1)

\n", "signature": "(self, item: Any) -> None:", "funcdef": "def"}, {"fullname": "paddles.queue.LinkedListQueue.dequeue", "modulename": "paddles.queue", "qualname": "LinkedListQueue.dequeue", "kind": "function", "doc": "

Remove and return the item at the front of the queue.

\n\n

Raise ValueError if the queue is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.stack", "modulename": "paddles.stack", "kind": "module", "doc": "

This module implements the Stack ADT.

\n\n

Intuition

\n\n

The Stack ADT models a pile of objects, e.g. a pile of storage boxes.\nOnly the object at the top of the pile can be accessed and removed.\nThe only way to add an object is to put it on top of the existing pile.

\n\n

Definition

\n\n

A stack is a sequence where members are removed from and added to\nthe same end of the sequence, called the top of the stack.

\n\n

A stack is a last-in, first-out (LIFO) sequence:\nthe next member to be removed is the one most recently added.

\n\n

A stack is a sequence ordered by age (time of addition).\nThe oldest member is at the bottom of the stack, and the youngest member is at the top.

\n\n

Operations

\n\n

The Stack ADT provides operations to:

\n\n
    \n
  • create a new empty stack
  • \n
  • add a new member, on top of the existing ones
  • \n
  • remove the member at the top of the stack
  • \n
  • access the member at the top of the stack without removing it
  • \n
  • compute the size of the stack (number of members).
  • \n
\n\n

Applications

\n\n

Stacks are used to implement function calls and depth-first search.\nYou should consider using a stack when you need to:

\n\n
    \n
  • simulate the handling of a pile of objects, like loading and unloading ship containers
  • \n
  • process nested structures, like brackets (e.g. print([1, {2, 3}])) or\nHTML tags (e.g. <p><b>text</b></p>)
  • \n
  • process items in the reverse order they were added, like undo operations\n(the next command to be undone is the most recently executed one).
  • \n
\n\n

Implementations

\n\n

The Stack ADT can be implemented with dynamic arrays or singly-linked lists.\nIn both cases, the operations listed above take constant time.\nA singly-linked list uses much more memory than a static array of the same length,\nbut a dynamic array may have wasted capacity and requires resizing.

\n"}, {"fullname": "paddles.stack.DynamicArrayStack", "modulename": "paddles.stack", "qualname": "DynamicArrayStack", "kind": "class", "doc": "

An implementation of the Stack ADT, using Python lists.

\n\n

Besides the ADT's operations, this class provides two convenience operations:

\n\n
    \n
  • create a non-empty stack from a given sequence
  • \n
  • convert a stack to a string, to see its members listed from bottom to top.
  • \n
\n\n
\n
>>> from paddles.stack import DynamicArrayStack\n>>> stack = DynamicArrayStack("abc")    # create a non-empty stack\n>>> stack.size()                        # number of members\n3\n>>> stack.pop()                         # remove and return the top member\n'c'\n>>> stack.peek()                        # return but don't remove the top member\n'b'\n>>> stack.push("C")                     # add a new member on top\n>>> print(stack)                        # str(stack) also possible\nDynamicArrayStack(['a', 'b', 'C'])\n
\n
\n"}, {"fullname": "paddles.stack.DynamicArrayStack.__init__", "modulename": "paddles.stack", "qualname": "DynamicArrayStack.__init__", "kind": "function", "doc": "

Initialize the stack with the members of sequence.

\n\n

The members are added to the stack in the order they are in sequence.\nTo create an empty stack, call DynamicArrayStack() or DynamicArrayStack([]).

\n\n

Complexity: O(len(sequence))

\n", "signature": "(sequence: collections.abc.Sequence[typing.Any] = [])"}, {"fullname": "paddles.stack.DynamicArrayStack.size", "modulename": "paddles.stack", "qualname": "DynamicArrayStack.size", "kind": "function", "doc": "

Return how many members the stack has.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "paddles.stack.DynamicArrayStack.peek", "modulename": "paddles.stack", "qualname": "DynamicArrayStack.peek", "kind": "function", "doc": "

Return the member at the top of the stack, without removing it.

\n\n

Raise ValueError if the stack is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.stack.DynamicArrayStack.push", "modulename": "paddles.stack", "qualname": "DynamicArrayStack.push", "kind": "function", "doc": "

Put item on top of the stack.

\n\n

Complexity: O(1)

\n", "signature": "(self, item: Any) -> None:", "funcdef": "def"}, {"fullname": "paddles.stack.DynamicArrayStack.pop", "modulename": "paddles.stack", "qualname": "DynamicArrayStack.pop", "kind": "function", "doc": "

Remove and return the member at the top of the stack.

\n\n

Raise ValueError if the stack is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.stack.LinkedListStack", "modulename": "paddles.stack", "qualname": "LinkedListStack", "kind": "class", "doc": "

An implementation of the Stack ADT, using singly-linked lists.

\n\n

Besides the ADT's operations, this class provides two convenience operations:

\n\n
    \n
  • create a non-empty stack from a given sequence
  • \n
  • convert a stack to a string, to see its members listed from bottom to top.
  • \n
\n\n
\n
>>> from paddles.stack import LinkedListStack\n>>> stack = LinkedListStack("abc")      # create a non-empty stack\n>>> stack.size()                        # number of members\n3\n>>> stack.pop()                         # remove and return the top member\n'c'\n>>> stack.peek()                        # return but don't remove the top member\n'b'\n>>> stack.push("C")                     # add a new member on top\n>>> print(stack)                        # str(stack) also possible\nLinkedListStack(['a', 'b', 'C'])\n
\n
\n"}, {"fullname": "paddles.stack.LinkedListStack.__init__", "modulename": "paddles.stack", "qualname": "LinkedListStack.__init__", "kind": "function", "doc": "

Initialize the stack with the members of sequence.

\n\n

The members are added to the stack in the order they are in sequence.\nTo create an empty stack, call LinkedListStack() or LinkedListStack([]).

\n\n

Complexity: O(len(sequence))

\n", "signature": "(sequence: collections.abc.Sequence[typing.Any] = [])"}, {"fullname": "paddles.stack.LinkedListStack.size", "modulename": "paddles.stack", "qualname": "LinkedListStack.size", "kind": "function", "doc": "

Return how many members the stack has.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "paddles.stack.LinkedListStack.push", "modulename": "paddles.stack", "qualname": "LinkedListStack.push", "kind": "function", "doc": "

Put item on top of the stack.

\n\n

Complexity: O(1)

\n", "signature": "(self, item: Any) -> None:", "funcdef": "def"}, {"fullname": "paddles.stack.LinkedListStack.pop", "modulename": "paddles.stack", "qualname": "LinkedListStack.pop", "kind": "function", "doc": "

Remove and return the member at the top of the stack.

\n\n

Raise ValueError if the stack is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}, {"fullname": "paddles.stack.LinkedListStack.peek", "modulename": "paddles.stack", "qualname": "LinkedListStack.peek", "kind": "function", "doc": "

Return the member at the top of the stack.

\n\n

Raise ValueError if the stack is empty.

\n\n

Complexity: O(1)

\n", "signature": "(self) -> Any:", "funcdef": "def"}]; + + // mirrored in build-search-index.js (part 1) + // Also split on html tags. this is a cheap heuristic, but good enough. + elasticlunr.tokenizer.setSeperator(/[\s\-.;&_'"=,()]+|<[^>]*>/); + + let searchIndex; + if (docs._isPrebuiltIndex) { + console.info("using precompiled search index"); + searchIndex = elasticlunr.Index.load(docs); + } else { + console.time("building search index"); + // mirrored in build-search-index.js (part 2) + searchIndex = elasticlunr(function () { + this.pipeline.remove(elasticlunr.stemmer); + this.pipeline.remove(elasticlunr.stopWordFilter); + this.addField("qualname"); + this.addField("fullname"); + this.addField("annotation"); + this.addField("default_value"); + this.addField("signature"); + this.addField("bases"); + this.addField("doc"); + this.setRef("fullname"); + }); + for (let doc of docs) { + searchIndex.addDoc(doc); + } + console.timeEnd("building search index"); + } + + return (term) => searchIndex.search(term, { + fields: { + qualname: {boost: 4}, + fullname: {boost: 2}, + annotation: {boost: 2}, + default_value: {boost: 2}, + signature: {boost: 2}, + bases: {boost: 2}, + doc: {boost: 1}, + }, + expand: true + }); +})(); \ No newline at end of file diff --git a/paddles/__init__.py b/paddles/__init__.py index e69de29..2a87b56 100644 --- a/paddles/__init__.py +++ b/paddles/__init__.py @@ -0,0 +1,14 @@ +"""`paddles` is a pedagogical algorithms and data structures library. + +It currently implements the Stack, Queue and Deque abstract data types (ADTs). + +The code repository is at https://github.com/dsa-ou/paddles. +The formatted documentation is at https://dsa-ou.github.io/paddles. + +## Licence + +`paddles` is Copyright © 2024 by The Open University, UK. +The code is licensed under a [BSD-3-clause licence](https://github.com/dsa-ou/paddles/blob/main/LICENSE). +The documentation is licensed under a +[Creative Commons Attribution 4.0 International Licence](http://creativecommons.org/licenses/by/4.0). +""" diff --git a/paddles/deque.py b/paddles/deque.py index 6d66a54..2f985e6 100644 --- a/paddles/deque.py +++ b/paddles/deque.py @@ -1,6 +1,51 @@ +"""This module implements the Deque ADT. + +## Intuition + +The Deque ADT models a line of objects that can be accessed, added to and +removed from either end of the line. +A deque can be used as a [stack](stack.html) or as a [queue](queue.html). + +## Definition + +A **deque**, pronounced 'deck' and short for 'double-ended queue', is a sequence +where only the members at both ends of the sequence +(called the **front** and the **back** of the queue) can be accessed and removed. +New members can only be added at the front or at the back. + +## Operations + +The Deque ADT provides operations to: +- create a new empty deque +- add a new member to the front of the deque +- add a new member to the back of the deque +- remove the member at the front of the deque +- remove the member at the back of the deque +- access the member at the front of the deque without removing it +- access the member at the back of the deque without removing it +- compute the size of the deque (number of members). + +## Applications + +Consider using a deque when you need to simulate a queue where +- objects jump the queue (join at the front) or + leave it from the back after waiting a certain time +- the direction of the queue changes, like cars on a ferry. + +## Implementations + +The Deque ADT can be implemented with circular dynamic arrays or doubly-linked lists. +In both cases, the operations listed above take constant time. +A doubly-linked list uses much more memory than a static array of the same length, +but a dynamic array may have wasted capacity and requires resizing. +`paddles` only provides a doubly-linked list implementation for the moment. +""" + from collections.abc import Sequence from typing import Any +__all__ = ["LinkedListDeque"] + # A doubly-linked list node is a list [previous, item, next]. # These constants make the code more readable. PREV = 0 @@ -9,36 +54,50 @@ class LinkedListDeque: - """Deque implementation using a doubly linked list for storage. + """An implementation of the Deque ADT, using a doubly-linked list. - >>> items = LinkedListDeque("abc") - >>> print(items) - LinkedListDeque(['a', 'b', 'c']) - >>> items.size() + Besides the ADT's operations, this class provides two convenience operations: + - create a non-empty deque from a given sequence + - convert a deque to a string, to see its members listed from front to back. + + >>> from paddles.deque import LinkedListDeque + >>> deque = LinkedListDeque("abc") # create a non-empty deque + >>> deque.size() # number of members 3 - >>> items.take_front() + >>> deque.take_front() # remove and return the front member 'a' - >>> items.take_back() + >>> deque.take_back() # remove and return the back member 'c' - >>> items.front() == items.back() == 'b' + >>> deque.front() == deque.back() == 'b' # return the front and back members True - >>> items.add_back("C") - >>> items.add_front("A") - >>> print(items) + >>> deque.add_back("C") # add a new member at the back + >>> deque.add_front("A") # add a new member at the front + >>> print(deque) # str(deque) also possible LinkedListDeque(['A', 'b', 'C']) """ - def __init__(self, iterable: Sequence[Any] | None = None) -> None: - """Initialize this deque with the given items, if any.""" + def __init__(self, sequence: Sequence[Any] = []) -> None: + """Initialize the deque with the members of `sequence`. + + The members are added to the deque in the order they are in `sequence`. + To create an empty deque, call `LinkedListDeque()` or `LinkedListDeque([])`. + + Complexity: O(len(`sequence`)) + """ self._head = None self._tail = None self._length = 0 - if iterable: - for item in iterable: + if sequence: + for item in sequence: self.add_back(item) def __str__(self) -> str: - """Return a formatted string representation of this deque.""" + """Return a string representation of the deque. + + The string is 'LinkedListDeque([front member, ..., back member])'. + + Complexity: O(self.size()) + """ strings = [] current = self._head while current: @@ -47,11 +106,41 @@ def __str__(self) -> str: return f"LinkedListDeque([{', '.join(strings)}])" def size(self) -> int: - """Return the number of items in this deque.""" + """Return how many members the deque has. + + Complexity: O(1) + """ return self._length + def front(self) -> Any: + """Return the item at the front of the deque, without removing it. + + Raise `ValueError` if the deque is empty. + + Complexity: O(1) + """ + if self.size() == 0: + msg = "can't access the front of an empty deque" + raise ValueError(msg) + return self._head[DATA] + + def back(self) -> Any: + """Return the item at the back of the deque, without removing it. + + Raise `ValueError` if the deque is empty. + + Complexity: O(1) + """ + if self.size() == 0: + msg = "can't access the back of an empty deque" + raise ValueError(msg) + return self._tail[DATA] + def add_front(self, item: Any) -> None: - """Insert the given item at the front of this deque.""" + """Put `item` at the front of the deque. + + Complexity: O(1) + """ node = [None, item, self._head] if self.size() == 0: self._tail = node @@ -61,7 +150,10 @@ def add_front(self, item: Any) -> None: self._length += 1 def add_back(self, item: Any) -> None: - """Insert the given item at the back of this deque.""" + """Put `item` at the back of the deque. + + Complexity: O(1) + """ node = [self._tail, item, None] if self.size() == 0: self._head = node @@ -71,12 +163,14 @@ def add_back(self, item: Any) -> None: self._length += 1 def take_front(self) -> Any: - """Remove and return the item at the front of this deque. + """Remove and return the item at the front of the deque. + + Raise `ValueError` if the deque is empty. - Raise ValueError if this deque is empty. + Complexity: O(1) """ if self.size() == 0: - msg = "can't take from an empty deque" + msg = "can't remove a member from an empty deque" raise ValueError(msg) item = self._head[DATA] self._head = self._head[NEXT] @@ -88,12 +182,14 @@ def take_front(self) -> Any: return item def take_back(self) -> Any: - """Remove and return the item at the back of this deque. + """Remove and return the item at the back of the deque. + + Raise `ValueError` if the deque is empty. - Raise ValueError if this deque is empty. + Complexity: O(1) """ if self.size() == 0: - msg = "can't take from an empty deque" + msg = "can't remove a member from an empty deque" raise ValueError(msg) item = self._tail[DATA] self._tail = self._tail[PREV] @@ -103,23 +199,3 @@ def take_back(self) -> Any: else: self._tail[NEXT] = None return item - - def front(self) -> Any: - """Return the item at the front of this deque without removing it. - - Raise ValueError if this deque is empty. - """ - if self.size() == 0: - msg = "Deque is empty" - raise ValueError(msg) - return self._head[DATA] - - def back(self) -> Any: - """Return the item at the back of this deque without removing it. - - Raise ValueError if this deque is empty. - """ - if self.size() == 0: - msg = "Deque is empty" - raise ValueError(msg) - return self._tail[DATA] diff --git a/paddles/queue.py b/paddles/queue.py index 73aa752..d9de481 100644 --- a/paddles/queue.py +++ b/paddles/queue.py @@ -1,38 +1,101 @@ +"""This module implements the Queue ADT. + +## Intuition + +The Queue ADT models a line of objects, e.g. cars waiting to board a ferry. +Only the object at the front of the line can be accessed and removed. +The only way to add an object is to put it at the back of the line. + +## Definition + +A **queue** is a sequence where members are added to one end of the sequence +(the **back** of the queue) and removed from the other end (the **front** of the queue). + +A queue is a **first-in, first-out (FIFO)** sequence: +the members are removed in the same order they were added. + +A queue is a sequence ordered by age (time of addition). +The oldest member is at the front of the queue, and the youngest member is at the back. + +## Operations + +The Queue ADT provides operations to: +- create a new empty queue +- add a new member, at the back of the existing ones +- remove the member at the front of the queue +- access the member at the front of the queue without removing it +- compute the size of the queue (number of members). + +## Applications + +Queues are used to implement breadth-first search. +You should consider using a queue when you need to: +- simulate a real-life queue, like travellers at passport control or + documents in a printer queue +- process items in the same order they were added, like a to-do list. + +## Implementations + +The Queue ADT can be implemented with circular dynamic arrays or singly-linked lists. +In both cases, the operations listed above take constant time. +A singly-linked list uses much more memory than a static array of the same length, +but a dynamic array may have wasted capacity and requires resizing. +`paddles` only provides a singly-linked list implementation for the moment. +""" + from collections.abc import Sequence from typing import Any -# A linked list node is a list [item, next]. +__all__ = ["LinkedListQueue"] + +# Each linked list node is a list [item, next]. # These constants make the code more readable. DATA = 0 NEXT = 1 class LinkedListQueue: - """A queue implemented using a linked list. + """An implementation of the Queue ADT, using a singly-linked list. + + Besides the ADT's operations, this class provides two convenience operations: + - create a non-empty queue from a given sequence + - convert a queue to a string, to see its members listed from front to back. - >>> q = LinkedListQueue("abc") - >>> q.size() + >>> from paddles.queue import LinkedListQueue + >>> q = LinkedListQueue("abc") # create a non-empty queue + >>> q.size() # number of members 3 - >>> q.dequeue() + >>> q.dequeue() # remove and return the front member 'a' - >>> q.front() + >>> q.front() # return but don't remove the front member 'b' - >>> q.enqueue("d") - >>> print(q) + >>> q.enqueue("d") # add a new member at the back + >>> print(q) # str(q) also possible LinkedListQueue(['b', 'c', 'd']) """ - def __init__(self, iterable: Sequence[Any] | None = None) -> None: - """Initialize this queue with the given items, if any.""" + def __init__(self, sequence: Sequence[Any] = []) -> None: + """Initialize the queue with the members of `sequence`. + + The members are added to the queue in the order they are in `sequence`. + To create an empty queue, call `LinkedListQueue()` or `LinkedListQueue([])`. + + Complexity: O(len(`sequence`)) + """ self._head = None self._tail = None self._length = 0 - if iterable: - for item in iterable: + if sequence: + for item in sequence: self.enqueue(item) def __str__(self) -> str: - """Return a formatted string representation of this queue.""" + """Return a string representation of the queue. + + The string is 'LinkedListQueue([front member, ..., back member])'. + + Complexity: O(self.size()) + """ strings = [] current = self._head while current: @@ -41,11 +104,29 @@ def __str__(self) -> str: return f"LinkedListQueue([{', '.join(strings)}])" def size(self) -> int: - """Return the number of items in this queue.""" + """Return how many members the queue has. + + Complexity: O(1) + """ return self._length + def front(self) -> Any: + """Return the member at the front of the queue, without removing it. + + Raise `ValueError` if the queue is empty. + + Complexity: O(1) + """ + if self.size() == 0: + msg = "can't access the front of an empty queue" + raise ValueError(msg) + return self._head[DATA] + def enqueue(self, item: Any) -> None: - """Insert the given item at the back of this queue.""" + """Put `item` at the back of the queue. + + Complexity: O(1) + """ node = [item, None] if self.size() == 0: self._head = node @@ -55,17 +136,12 @@ def enqueue(self, item: Any) -> None: self._tail = node self._length += 1 - def front(self) -> Any: - """Return the item at the front of this queue without removing it.""" - if self.size() == 0: - msg = "can't access front of an empty queue" - raise ValueError(msg) - return self._head[DATA] - def dequeue(self) -> Any: - """Remove and return the item at the front of this queue, + """Remove and return the item at the front of the queue. + + Raise `ValueError` if the queue is empty. - or raise ValueError if this queue is empty. + Complexity: O(1) """ if self.size() == 0: msg = "can't dequeue from an empty queue" diff --git a/paddles/stack.py b/paddles/stack.py index 1e689f9..ff596a7 100644 --- a/paddles/stack.py +++ b/paddles/stack.py @@ -1,63 +1,133 @@ +"""This module implements the Stack ADT. + +## Intuition + +The Stack ADT models a pile of objects, e.g. a pile of storage boxes. +Only the object at the top of the pile can be accessed and removed. +The only way to add an object is to put it on top of the existing pile. + +## Definition + +A **stack** is a sequence where members are removed from and added to +the same end of the sequence, called the **top** of the stack. + +A stack is a **last-in, first-out (LIFO)** sequence: +the next member to be removed is the one most recently added. + +A stack is a sequence ordered by age (time of addition). +The oldest member is at the bottom of the stack, and the youngest member is at the top. + +## Operations + +The Stack ADT provides operations to: +- create a new empty stack +- add a new member, on top of the existing ones +- remove the member at the top of the stack +- access the member at the top of the stack without removing it +- compute the size of the stack (number of members). + +## Applications + +Stacks are used to implement function calls and depth-first search. +You should consider using a stack when you need to: +- simulate the handling of a pile of objects, like loading and unloading ship containers +- process nested structures, like brackets (e.g. `print([1, {2, 3}])`) or + HTML tags (e.g. `

text

`) +- process items in the reverse order they were added, like undo operations + (the next command to be undone is the most recently executed one). + +## Implementations + +The Stack ADT can be implemented with dynamic arrays or singly-linked lists. +In both cases, the operations listed above take constant time. +A singly-linked list uses much more memory than a static array of the same length, +but a dynamic array may have wasted capacity and requires resizing. +""" + from collections.abc import Sequence from typing import Any +__all__ = ["DynamicArrayStack", "LinkedListStack"] + class DynamicArrayStack: - """A stack implementation using Python lists. + """An implementation of the Stack ADT, using Python lists. + + Besides the ADT's operations, this class provides two convenience operations: + - create a non-empty stack from a given sequence + - convert a stack to a string, to see its members listed from bottom to top. - >>> stack = DynamicArrayStack("abc") - >>> stack.size() + >>> from paddles.stack import DynamicArrayStack + >>> stack = DynamicArrayStack("abc") # create a non-empty stack + >>> stack.size() # number of members 3 - >>> stack.pop() + >>> stack.pop() # remove and return the top member 'c' - >>> stack.peek() + >>> stack.peek() # return but don't remove the top member 'b' - >>> stack.push("C") - >>> print(stack) + >>> stack.push("C") # add a new member on top + >>> print(stack) # str(stack) also possible DynamicArrayStack(['a', 'b', 'C']) """ - def __init__(self, iterable: Sequence[Any] | None = None) -> None: - """Initialize this stack with the given items, if any.""" - if iterable: - self._members = list(iterable) - else: - self._members = [] + def __init__(self, sequence: Sequence[Any] = []) -> None: + """Initialize the stack with the members of `sequence`. + + The members are added to the stack in the order they are in `sequence`. + To create an empty stack, call `DynamicArrayStack()` or `DynamicArrayStack([])`. + + Complexity: O(len(`sequence`)) + """ + self._members = [] + for item in sequence: + self.push(item) def __str__(self) -> str: - """Return a string representation of this stack. + """Return a string representation of the stack. The string is 'DynamicArrayStack([bottom member, ..., top member])'. + + Complexity: O(self.size()) """ return f"DynamicArrayStack({self._members})" def size(self) -> int: - """Return how many members this stack has.""" + """Return how many members the stack has. + + Complexity: O(1) + """ return len(self._members) - def push(self, item: Any) -> None: - """Add the given item to the top of this stack.""" - self._members.append(item) + def peek(self) -> Any: + """Return the member at the top of the stack, without removing it. - def pop(self) -> Any: - """Remove and return the top item of this stack. + Raise `ValueError` if the stack is empty. - Raise ValueError if this stack is empty. + Complexity: O(1) """ if self.size() == 0: - msg = "can't pop a member from an empty stack" + msg = "can't peek into an empty stack" raise ValueError(msg) - return self._members.pop() + return self._members[-1] - def peek(self) -> Any: - """Return the top item of this stack. + def push(self, item: Any) -> None: + """Put `item` on top of the stack. - Raise ValueError if this stack is empty. + Complexity: O(1) + """ + self._members.append(item) + + def pop(self) -> Any: + """Remove and return the member at the top of the stack. + + Raise `ValueError` if the stack is empty. + + Complexity: O(1) """ if self.size() == 0: - msg = "can't peek into an empty stack" + msg = "can't pop a member from an empty stack" raise ValueError(msg) - return self._members[-1] + return self._members.pop() # Each linked list node is a tuple (data, next). @@ -67,32 +137,44 @@ def peek(self) -> Any: class LinkedListStack: - """A stack implementation using singly-linked lists. + """An implementation of the Stack ADT, using singly-linked lists. - >>> stack = LinkedListStack("abc") - >>> stack.size() + Besides the ADT's operations, this class provides two convenience operations: + - create a non-empty stack from a given sequence + - convert a stack to a string, to see its members listed from bottom to top. + + >>> from paddles.stack import LinkedListStack + >>> stack = LinkedListStack("abc") # create a non-empty stack + >>> stack.size() # number of members 3 - >>> stack.pop() + >>> stack.pop() # remove and return the top member 'c' - >>> stack.peek() + >>> stack.peek() # return but don't remove the top member 'b' - >>> stack.push("C") - >>> print(stack) + >>> stack.push("C") # add a new member on top + >>> print(stack) # str(stack) also possible LinkedListStack(['a', 'b', 'C']) """ - def __init__(self, iterable: Sequence[Any] | None = None) -> None: - """Initialize this stack with the given items, if any.""" + def __init__(self, sequence: Sequence[Any] = []) -> None: + """Initialize the stack with the members of `sequence`. + + The members are added to the stack in the order they are in `sequence`. + To create an empty stack, call `LinkedListStack()` or `LinkedListStack([])`. + + Complexity: O(len(`sequence`)) + """ self._head = None self._length = 0 - if iterable: - for item in iterable: - self.push(item) + for item in sequence: + self.push(item) def __str__(self) -> str: - """Return a string representation of this stack. + """Return a string representation of the stack. The string is 'LinkedListStack([bottom member, ..., top member])'. + + Complexity: O(self.size()) """ strings = [] current = self._head @@ -102,18 +184,26 @@ def __str__(self) -> str: return "LinkedListStack([" + ", ".join(reversed(strings)) + "])" def size(self) -> int: - """Return how many members this stack has.""" + """Return how many members the stack has. + + Complexity: O(1) + """ return self._length def push(self, item: Any) -> None: - """Add the given item to the top of this stack.""" + """Put `item` on top of the stack. + + Complexity: O(1) + """ self._head = (item, self._head) self._length += 1 def pop(self) -> Any: - """Remove and return the item on the top of this stack. + """Remove and return the member at the top of the stack. + + Raise `ValueError` if the stack is empty. - Raise ValueError if this stack is empty. + Complexity: O(1) """ if self.size() == 0: msg = "can't pop a member from an empty stack" @@ -124,9 +214,11 @@ def pop(self) -> Any: return item def peek(self) -> Any: - """Return the item on the top of this stack. + """Return the member at the top of the stack. + + Raise `ValueError` if the stack is empty. - Raise ValueError if this stack is empty. + Complexity: O(1) """ if self.size() == 0: msg = "can't peek into an empty stack" diff --git a/poetry.lock b/poetry.lock index e01ff81..d532560 100644 --- a/poetry.lock +++ b/poetry.lock @@ -403,6 +403,25 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] +[[package]] +name = "pdoc" +version = "14.3.0" +description = "API Documentation for Python Projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pdoc-14.3.0-py3-none-any.whl", hash = "sha256:9a8f9a48bda5a99c249367c2b99779dbdd9f4a56f905068c9c2d6868dbae6882"}, + {file = "pdoc-14.3.0.tar.gz", hash = "sha256:40bf8f092fcd91560d5e6cebb7c21b65df699f90a468c8ea316235c3368d5449"}, +] + +[package.dependencies] +Jinja2 = ">=2.11.0" +MarkupSafe = "*" +pygments = ">=2.12.0" + +[package.extras] +dev = ["hypothesis", "mypy", "pdoc-pyo3-sample-library (==1.0.11)", "pygments (>=2.14.0)", "pytest", "pytest-cov", "pytest-timeout", "ruff", "tox", "types-pygments"] + [[package]] name = "platformdirs" version = "4.1.0" @@ -481,6 +500,21 @@ dev = ["black", "chardet"] release = ["zest.releaser[recommended]"] tests = ["black", "chardet", "tox"] +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pyparsing" version = "3.1.1" @@ -756,4 +790,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "1f184fd249a04f522982bd5c412fe84090a94d1dbc36a2198850980312bc4e5a" +content-hash = "a87613c91c03d734e344dcd5c134b369af11d777c8134129973247daf1bfbe04" diff --git a/pyproject.toml b/pyproject.toml index 0d5cf7c..8d859f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ pytest-cov = "^4.1.0" pre-commit = "^3.6.0" ruff = "^0.1.9" pytype = "^2023.12.18" +pdoc = "^14.3.0" [build-system] requires = ["poetry-core"] @@ -27,13 +28,15 @@ target-version = "py310" [tool.ruff.lint] select = ["ALL"] -ignore = [ - "ANN101", # don't require type annotations for `self` - "ANN401", # don't flag annotations with `Any` - "D", # don't flag missing documentation yet - "ISC001", "COM812", # ignore rules that conflict with formatter +ignore = [ # ignore these rules: + "ANN101", # provide the type of `self` + "ANN401", # don't use `Any` + "ISC001", "COM812", # these rules may conflict with the formatter ] +[tool.ruff.lint.pydocstyle] +convention = "pep257" # this project's docstrings follow PEP 257 + [tool.ruff.lint.per-file-ignores] -# don't flag assertions in test code: they're used by pytest -"test_*.py" = ["S101"] +# in test code, don't flag assertions nor missing package docstrings +"tests/*py" = ["S101", "D104"] diff --git a/tests/test_deque.py b/tests/test_deque.py index d094856..3ff6871 100644 --- a/tests/test_deque.py +++ b/tests/test_deque.py @@ -13,13 +13,13 @@ def check_is_empty(deque: DequeADT) -> None: """Test that the deque is empty.""" assert deque.size() == 0 - with pytest.raises(ValueError, match="Deque is empty"): + with pytest.raises(ValueError, match="can't access the front of an empty deque"): deque.front() - with pytest.raises(ValueError, match="Deque is empty"): + with pytest.raises(ValueError, match="can't access the back of an empty deque"): deque.back() - with pytest.raises(ValueError, match="can't take from an empty deque"): + with pytest.raises(ValueError, match="can't remove a member from an empty deque"): deque.take_front() - with pytest.raises(ValueError, match="can't take from an empty deque"): + with pytest.raises(ValueError, match="can't remove a member from an empty deque"): deque.take_back() assert str(deque) == f"{deque.__class__.__name__}([])" diff --git a/tests/test_queue.py b/tests/test_queue.py index 41ba0ae..6ed6be1 100644 --- a/tests/test_queue.py +++ b/tests/test_queue.py @@ -14,7 +14,7 @@ def check_is_empty(queue: QueueADT) -> None: """Test that the queue is empty.""" assert queue.size() == 0 - with pytest.raises(ValueError, match="can't access front of an empty queue"): + with pytest.raises(ValueError, match="can't access the front of an empty queue"): queue.front() with pytest.raises(ValueError, match="can't dequeue from an empty queue"): queue.dequeue()