pional 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pional/__init__.py +9 -0
- pional/adts/__init__.py +8 -0
- pional/adts/option.py +173 -0
- pional/adts/result.py +251 -0
- pional/collections/__init__.py +10 -0
- pional/collections/base.py +20 -0
- pional/collections/hashmap.py +90 -0
- pional/collections/hashset.py +61 -0
- pional/collections/vec.py +89 -0
- pional/fn/__init__.py +26 -0
- pional/fn/compose.py +41 -0
- pional/fn/pipe.py +37 -0
- pional/iterators/__init__.py +31 -0
- pional/iterators/adapters.py +150 -0
- pional/iterators/base.py +507 -0
- pional/iterators/combinators.py +34 -0
- pional/iterators/indexed.py +51 -0
- pional/iterators/observers.py +47 -0
- pional/iterators/order.py +50 -0
- pional-0.1.0.dist-info/METADATA +108 -0
- pional-0.1.0.dist-info/RECORD +22 -0
- pional-0.1.0.dist-info/WHEEL +4 -0
pional/__init__.py
ADDED
pional/adts/__init__.py
ADDED
pional/adts/option.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Option type — represents an optional value.
|
|
2
|
+
|
|
3
|
+
Provides ``Some[T]`` for present values and ``Nothing`` for absent values,
|
|
4
|
+
with monadic operations like ``map`` and ``and_then``.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import final, Callable, Any, TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .result import Result
|
|
12
|
+
|
|
13
|
+
type Option[T] = Some[T] | Nothing
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@final
|
|
17
|
+
class Some[T]:
|
|
18
|
+
"""Represents a present value."""
|
|
19
|
+
|
|
20
|
+
__match_args__ = ('value',)
|
|
21
|
+
|
|
22
|
+
def __init__(self, value: T) -> None:
|
|
23
|
+
self.value = value
|
|
24
|
+
|
|
25
|
+
def unwrap(self) -> T:
|
|
26
|
+
"""Return the inner value."""
|
|
27
|
+
return self.value
|
|
28
|
+
|
|
29
|
+
def unwrap_or(self, default: T) -> T:
|
|
30
|
+
"""Return the inner value, ignoring *default*."""
|
|
31
|
+
return self.value
|
|
32
|
+
|
|
33
|
+
def map[U](self, f: Callable[[T], U]) -> Some[U]:
|
|
34
|
+
"""Transform the inner value using *f*.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
f: The function to apply to the wrapped value.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
A new ``Some`` containing the transformed value.
|
|
41
|
+
"""
|
|
42
|
+
return Some(f(self.value))
|
|
43
|
+
|
|
44
|
+
def and_then[U](self, f: Callable[[T], Option[U]]) -> Option[U]:
|
|
45
|
+
"""Apply *f* to the inner value and return the result.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
f: A function that returns an ``Option``.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
The ``Option`` returned by *f*.
|
|
52
|
+
"""
|
|
53
|
+
return f(self.value)
|
|
54
|
+
|
|
55
|
+
def ok_or[E](self, err: E) -> Result[T, E]:
|
|
56
|
+
"""Convert to ``Ok``, ignoring *err*.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
err: The error value (discarded).
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
``Ok(self.value)``.
|
|
63
|
+
"""
|
|
64
|
+
from .result import Ok as ResultOk
|
|
65
|
+
return ResultOk(self.value)
|
|
66
|
+
|
|
67
|
+
def ok_or_else[E](self, err_fn: Callable[[], E]) -> Result[T, E]:
|
|
68
|
+
"""Convert to ``Ok``, ignoring *err_fn*.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
err_fn: A function producing an error (not called).
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
``Ok(self.value)``.
|
|
75
|
+
"""
|
|
76
|
+
from .result import Ok as ResultOk
|
|
77
|
+
return ResultOk(self.value)
|
|
78
|
+
|
|
79
|
+
def is_some(self) -> bool:
|
|
80
|
+
"""True if this is a ``Some`` value."""
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
def is_none(self) -> bool:
|
|
84
|
+
"""False — this is a ``Some``."""
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
def __repr__(self) -> str:
|
|
88
|
+
return f'Some({self.value!r})'
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@final
|
|
92
|
+
class Nothing:
|
|
93
|
+
"""Represents an absent value (singleton)."""
|
|
94
|
+
|
|
95
|
+
__match_args__ = ()
|
|
96
|
+
_instance: Nothing | None = None
|
|
97
|
+
|
|
98
|
+
def __new__(cls) -> Nothing:
|
|
99
|
+
if cls._instance is None:
|
|
100
|
+
cls._instance = super().__new__(cls)
|
|
101
|
+
return cls._instance
|
|
102
|
+
|
|
103
|
+
def unwrap(self) -> None:
|
|
104
|
+
"""Raise — cannot unwrap ``Nothing``."""
|
|
105
|
+
raise ValueError('Called unwrap on Nothing')
|
|
106
|
+
|
|
107
|
+
def unwrap_or[T](self, default: T) -> T:
|
|
108
|
+
"""Return *default*.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
default: The fallback value.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
*default* unchanged.
|
|
115
|
+
"""
|
|
116
|
+
return default
|
|
117
|
+
|
|
118
|
+
def ok_or[T, E](self, err: E) -> Result[T, E]:
|
|
119
|
+
"""Convert to ``Err`` using *err*.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
err: The error value.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
``Err(err)``.
|
|
126
|
+
"""
|
|
127
|
+
from .result import Err as ResultErr
|
|
128
|
+
return ResultErr(err)
|
|
129
|
+
|
|
130
|
+
def ok_or_else[T, E](self, err_fn: Callable[[], E]) -> Result[T, E]:
|
|
131
|
+
"""Convert to ``Err`` by calling *err_fn*.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
err_fn: A function producing the error value.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
``Err(err_fn())``.
|
|
138
|
+
"""
|
|
139
|
+
from .result import Err as ResultErr
|
|
140
|
+
return ResultErr(err_fn())
|
|
141
|
+
|
|
142
|
+
def map[U](self, f: Callable[[Any], U]) -> Nothing:
|
|
143
|
+
"""No-op — returns ``Nothing`` unchanged.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
f: The function (not called).
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
``self`` (``Nothing``).
|
|
150
|
+
"""
|
|
151
|
+
return self
|
|
152
|
+
|
|
153
|
+
def and_then[U](self, f: Callable[[Any], Option[U]]) -> Nothing:
|
|
154
|
+
"""No-op — returns ``Nothing`` unchanged.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
f: The function (not called).
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
``self`` (``Nothing``).
|
|
161
|
+
"""
|
|
162
|
+
return self
|
|
163
|
+
|
|
164
|
+
def is_some(self) -> bool:
|
|
165
|
+
"""False — this is ``Nothing``."""
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
def is_none(self) -> bool:
|
|
169
|
+
"""True if this is ``Nothing``."""
|
|
170
|
+
return True
|
|
171
|
+
|
|
172
|
+
def __repr__(self) -> str:
|
|
173
|
+
return 'Nothing'
|
pional/adts/result.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Result type — represents success (``Ok``) or failure (``Err``).
|
|
2
|
+
|
|
3
|
+
Provides monadic operations and combinators for working with fallible
|
|
4
|
+
computations in a type-safe way.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import final, Callable, Any, TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .option import Option, Nothing
|
|
12
|
+
|
|
13
|
+
type Result[T, E] = Ok[T] | Err[E]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@final
|
|
17
|
+
class Ok[T]:
|
|
18
|
+
"""Represents a successful result containing a value."""
|
|
19
|
+
|
|
20
|
+
__match_args__ = ('value',)
|
|
21
|
+
|
|
22
|
+
def __init__(self, value: T) -> None:
|
|
23
|
+
self.value = value
|
|
24
|
+
|
|
25
|
+
def unwrap(self) -> T:
|
|
26
|
+
"""Return the inner value."""
|
|
27
|
+
return self.value
|
|
28
|
+
|
|
29
|
+
def unwrap_or(self, default: T) -> T:
|
|
30
|
+
"""Return the inner value, ignoring *default*."""
|
|
31
|
+
return self.value
|
|
32
|
+
|
|
33
|
+
def map[U](self, f: Callable[[T], U]) -> Ok[U]:
|
|
34
|
+
"""Transform the inner value using *f*.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
f: The function to apply.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
``Ok(f(self.value))``.
|
|
41
|
+
"""
|
|
42
|
+
return Ok(f(self.value))
|
|
43
|
+
|
|
44
|
+
def map_err(self, f: Callable[[Any], Any]) -> Ok[T]:
|
|
45
|
+
"""No-op — returns ``self`` unchanged."""
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def and_then[U, F](self, f: Callable[[T], Result[U, F]]) -> Result[U, F]:
|
|
49
|
+
"""Apply *f* to the inner value and return the result.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
f: A function returning a ``Result``.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The ``Result`` returned by *f*.
|
|
56
|
+
"""
|
|
57
|
+
return f(self.value)
|
|
58
|
+
|
|
59
|
+
def ok(self) -> Option[T]:
|
|
60
|
+
"""Convert to ``Some(self.value)``."""
|
|
61
|
+
from .option import Some as OptionSome
|
|
62
|
+
return OptionSome(self.value)
|
|
63
|
+
|
|
64
|
+
def err(self) -> Nothing:
|
|
65
|
+
"""Always returns ``Nothing`` — this is an ``Ok``."""
|
|
66
|
+
from .option import Nothing as OptionNothing
|
|
67
|
+
return OptionNothing()
|
|
68
|
+
|
|
69
|
+
def map_or[U](self, default: U, f: Callable[[T], U]) -> U:
|
|
70
|
+
"""Apply *f* to the inner value, ignoring *default*.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
default: The fallback (ignored).
|
|
74
|
+
f: The function to apply.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
``f(self.value)``.
|
|
78
|
+
"""
|
|
79
|
+
return f(self.value)
|
|
80
|
+
|
|
81
|
+
def map_or_else[U](self, default: Callable[[Any], U], f: Callable[[T], U]) -> U:
|
|
82
|
+
"""Apply *f* to the inner value, ignoring *default*.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
default: The fallback function (not called).
|
|
86
|
+
f: The function to apply.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
``f(self.value)``.
|
|
90
|
+
"""
|
|
91
|
+
return f(self.value)
|
|
92
|
+
|
|
93
|
+
def combine[U, E](self, other: Result[U, E]) -> Result[tuple[T, U], E]:
|
|
94
|
+
"""Pair this ``Ok`` with *other*.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
other: Another ``Result``.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
``Ok((self.value, v))`` if *other* is ``Ok(v)``,
|
|
101
|
+
otherwise propagates the ``Err``.
|
|
102
|
+
"""
|
|
103
|
+
match other:
|
|
104
|
+
case Ok(v):
|
|
105
|
+
return Ok((self.value, v))
|
|
106
|
+
case Err(e):
|
|
107
|
+
return Err(e)
|
|
108
|
+
|
|
109
|
+
def combine_with[U, V, E](self, other: Result[U, E], f: Callable[[T, U], V]) -> Result[V, E]:
|
|
110
|
+
"""Combine this ``Ok`` with *other* using *f*.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
other: Another ``Result``.
|
|
114
|
+
f: The merge function.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
``Ok(f(self.value, v))`` if *other* is ``Ok(v)``,
|
|
118
|
+
otherwise propagates the ``Err``.
|
|
119
|
+
"""
|
|
120
|
+
match other:
|
|
121
|
+
case Ok(v):
|
|
122
|
+
return Ok(f(self.value, v))
|
|
123
|
+
case Err(e):
|
|
124
|
+
return Err(e)
|
|
125
|
+
|
|
126
|
+
def is_ok(self) -> bool:
|
|
127
|
+
"""True if this is an ``Ok`` value."""
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
def is_err(self) -> bool:
|
|
131
|
+
"""False — this is an ``Ok``."""
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
def __repr__(self) -> str:
|
|
135
|
+
return f'Ok({self.value!r})'
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@final
|
|
139
|
+
class Err[E]:
|
|
140
|
+
"""Represents a failure result containing an error value."""
|
|
141
|
+
|
|
142
|
+
__match_args__ = ('value',)
|
|
143
|
+
|
|
144
|
+
def __init__(self, value: E) -> None:
|
|
145
|
+
self.value = value
|
|
146
|
+
|
|
147
|
+
def unwrap(self) -> None:
|
|
148
|
+
"""Raise — cannot unwrap an ``Err``."""
|
|
149
|
+
raise ValueError(f'Called unwrap on Err: {self.value}')
|
|
150
|
+
|
|
151
|
+
def unwrap_or[T](self, default: T) -> T:
|
|
152
|
+
"""Return *default*.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
default: The fallback value.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
*default* unchanged.
|
|
159
|
+
"""
|
|
160
|
+
return default
|
|
161
|
+
|
|
162
|
+
def unwrap_err(self) -> E:
|
|
163
|
+
"""Return the inner error value."""
|
|
164
|
+
return self.value
|
|
165
|
+
|
|
166
|
+
def map[U](self, f: Callable[[Any], U]) -> Err[E]:
|
|
167
|
+
"""No-op — returns ``self`` unchanged."""
|
|
168
|
+
return self
|
|
169
|
+
|
|
170
|
+
def map_err[F](self, f: Callable[[E], F]) -> Err[F]:
|
|
171
|
+
"""Transform the error value using *f*.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
f: The function to apply.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
``Err(f(self.value))``.
|
|
178
|
+
"""
|
|
179
|
+
return Err(f(self.value))
|
|
180
|
+
|
|
181
|
+
def and_then[U](self, f: Callable[[Any], Result[U, Any]]) -> Err[E]:
|
|
182
|
+
"""No-op — returns ``self`` unchanged."""
|
|
183
|
+
return self
|
|
184
|
+
|
|
185
|
+
def ok(self) -> Nothing:
|
|
186
|
+
"""Always returns ``Nothing`` — this is an ``Err``."""
|
|
187
|
+
from .option import Nothing as OptionNothing
|
|
188
|
+
return OptionNothing()
|
|
189
|
+
|
|
190
|
+
def err(self) -> Option[E]:
|
|
191
|
+
"""Convert to ``Some(self.value)``."""
|
|
192
|
+
from .option import Some as OptionSome
|
|
193
|
+
return OptionSome(self.value)
|
|
194
|
+
|
|
195
|
+
def map_or[T, U](self, default: U, f: Callable[[T], U]) -> U:
|
|
196
|
+
"""Return *default*, ignoring *f*.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
default: The fallback value.
|
|
200
|
+
f: The function (not called).
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
*default* unchanged.
|
|
204
|
+
"""
|
|
205
|
+
return default
|
|
206
|
+
|
|
207
|
+
def map_or_else[U](self, default: Callable[[E], U], f: Callable[[Any], U]) -> U:
|
|
208
|
+
"""Call *default* with the error value.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
default: The fallback function.
|
|
212
|
+
f: The function (not called).
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
``default(self.value)``.
|
|
216
|
+
"""
|
|
217
|
+
return default(self.value)
|
|
218
|
+
|
|
219
|
+
def combine[T, U](self, other: Result[U, E]) -> Result[tuple[T, U], E]:
|
|
220
|
+
"""Propagate this ``Err`` — ignores *other*.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
other: Another ``Result`` (ignored).
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
``self`` (the error).
|
|
227
|
+
"""
|
|
228
|
+
return self # type: ignore
|
|
229
|
+
|
|
230
|
+
def combine_with[T, U, V](self, other: Result[U, E], f: Callable[[T, U], V]) -> Result[V, E]:
|
|
231
|
+
"""Propagate this ``Err`` — ignores *other* and *f*.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
other: Another ``Result`` (ignored).
|
|
235
|
+
f: The merge function (not called).
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
``self`` (the error).
|
|
239
|
+
"""
|
|
240
|
+
return self # type: ignore
|
|
241
|
+
|
|
242
|
+
def is_ok(self) -> bool:
|
|
243
|
+
"""False — this is an ``Err``."""
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
def is_err(self) -> bool:
|
|
247
|
+
"""True if this is an ``Err`` value."""
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
def __repr__(self) -> str:
|
|
251
|
+
return f'Err({self.value!r})'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Base abstraction for collection types."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING, Self
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from pional.iterators import IteratorExt, Iter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CollectionExt[T](ABC):
|
|
11
|
+
"""Abstract base for collections that can be constructed from iterators."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def into_iter(self) -> Iter[T]:
|
|
15
|
+
"""Consume and convert into an ``Iter``."""
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def from_iter(cls, iterator: IteratorExt[T]) -> Self:
|
|
20
|
+
"""Construct an instance from an ``IteratorExt``."""
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""HashMap — a hash map wrapping ``dict``."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from collections.abc import (
|
|
4
|
+
Iterable,
|
|
5
|
+
MutableMapping,
|
|
6
|
+
Mapping,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pional.iterators import Iter, IteratorExt
|
|
12
|
+
from pional.adts import Option, Some, Nothing
|
|
13
|
+
|
|
14
|
+
from .base import CollectionExt
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HashMap[K, V](MutableMapping[K, V], CollectionExt[tuple[K, V]]):
|
|
18
|
+
"""A mutable hash map backed by ``dict``."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
items: Mapping[K, V] | Iterable[tuple[K, V]] = (),
|
|
23
|
+
):
|
|
24
|
+
self._data: dict[K, V] = dict(items)
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
return f"HashMap({self._data})"
|
|
28
|
+
|
|
29
|
+
def __repr__(self) -> str:
|
|
30
|
+
return f"HashMap({self._data!r})"
|
|
31
|
+
|
|
32
|
+
def __getitem__(self, key: K) -> V:
|
|
33
|
+
return self._data[key]
|
|
34
|
+
|
|
35
|
+
def __setitem__(self, key: K, value: V) -> None:
|
|
36
|
+
self._data[key] = value
|
|
37
|
+
|
|
38
|
+
def __delitem__(self, key: K) -> None:
|
|
39
|
+
del self._data[key]
|
|
40
|
+
|
|
41
|
+
def __iter__(self) -> Iter[tuple[K, V]]:
|
|
42
|
+
return self.into_iter()
|
|
43
|
+
|
|
44
|
+
def __len__(self) -> int:
|
|
45
|
+
return len(self._data)
|
|
46
|
+
|
|
47
|
+
def keys(self) -> Iter[K]:
|
|
48
|
+
"""Return an iterator over keys."""
|
|
49
|
+
return Iter(self._data.keys())
|
|
50
|
+
|
|
51
|
+
def values(self) -> Iter[V]:
|
|
52
|
+
"""Return an iterator over values."""
|
|
53
|
+
return Iter(self._data.values())
|
|
54
|
+
|
|
55
|
+
def into_iter(self) -> Iter[tuple[K, V]]:
|
|
56
|
+
"""Consume and convert into an ``Iter`` of ``(key, value)`` pairs."""
|
|
57
|
+
from pional.iterators import Iter
|
|
58
|
+
return Iter(self._data.items())
|
|
59
|
+
|
|
60
|
+
def insert(self, key: K, value: V) -> None:
|
|
61
|
+
"""Insert a key-value pair.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
key: The key.
|
|
65
|
+
value: The associated value.
|
|
66
|
+
"""
|
|
67
|
+
self._data[key] = value
|
|
68
|
+
|
|
69
|
+
def remove(self, key: K) -> Option[V]:
|
|
70
|
+
"""Remove the value associated with *key*.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
key: The key to remove.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
``Some(V)`` if the key existed, ``Nothing`` otherwise.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
return Some(self._data.pop(key))
|
|
80
|
+
except KeyError:
|
|
81
|
+
return Nothing()
|
|
82
|
+
|
|
83
|
+
def contains(self, key: K) -> bool:
|
|
84
|
+
"""True if *key* exists in the map."""
|
|
85
|
+
return key in self._data
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def from_iter(cls, iterator: IteratorExt[tuple[K, V]]) -> HashMap[K, V]:
|
|
89
|
+
"""Construct a ``HashMap`` from an ``IteratorExt`` of pairs."""
|
|
90
|
+
return HashMap(iterator)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""HashSet — a hash set wrapping ``set``."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from collections.abc import (
|
|
4
|
+
Iterable,
|
|
5
|
+
MutableSet,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pional.iterators import Iter, IteratorExt
|
|
12
|
+
|
|
13
|
+
from .base import CollectionExt
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HashSet[T](MutableSet[T], CollectionExt[T]):
|
|
17
|
+
"""A mutable hash set backed by ``set``."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, items: Iterable[T] = ()):
|
|
20
|
+
self._set: set[T] = set(items)
|
|
21
|
+
|
|
22
|
+
def __str__(self) -> str:
|
|
23
|
+
return f"HashSet({self._set})"
|
|
24
|
+
|
|
25
|
+
def __repr__(self) -> str:
|
|
26
|
+
return f"HashMap({self._set!r})"
|
|
27
|
+
|
|
28
|
+
def __contains__(self, item: T) -> bool:
|
|
29
|
+
return item in self._set
|
|
30
|
+
|
|
31
|
+
def __iter__(self) -> Iter[T]:
|
|
32
|
+
return self.into_iter()
|
|
33
|
+
|
|
34
|
+
def __len__(self) -> int:
|
|
35
|
+
return len(self._set)
|
|
36
|
+
|
|
37
|
+
def add(self, value: T) -> None:
|
|
38
|
+
"""Add *value* to the set."""
|
|
39
|
+
self._set.add(value)
|
|
40
|
+
|
|
41
|
+
def insert(self, value: T) -> None:
|
|
42
|
+
"""Alias for ``add``."""
|
|
43
|
+
self._set.add(value)
|
|
44
|
+
|
|
45
|
+
def contains(self, value: T) -> bool:
|
|
46
|
+
"""True if *value* is in the set."""
|
|
47
|
+
return value in self._set
|
|
48
|
+
|
|
49
|
+
def discard(self, value: T) -> None:
|
|
50
|
+
"""Remove *value* if present, otherwise no-op."""
|
|
51
|
+
self._set.discard(value)
|
|
52
|
+
|
|
53
|
+
def into_iter(self) -> Iter[T]:
|
|
54
|
+
"""Consume and convert into an ``Iter``."""
|
|
55
|
+
from pional.iterators import Iter
|
|
56
|
+
return Iter(self._set)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_iter(cls, iterator: IteratorExt[T]) -> HashSet[T]:
|
|
60
|
+
"""Construct a ``HashSet`` from an ``IteratorExt``."""
|
|
61
|
+
return HashSet(iterator)
|