pythonic-fp.containers 2.0.0__py3-none-any.whl → 3.0.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.
@@ -12,7 +12,23 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """Developer Tools - Container-like data structures"""
15
+ """Package of container like data structures.
16
+
17
+ +------------------+--------+----------------------------------------------------+
18
+ | Description | Class | Package/Module |
19
+ +==================+========+====================================================+
20
+ | Functional tuple | FTuple | module ``pythonic_fp.containers.functional_tuple`` |
21
+ +------------------+--------+----------------------------------------------------+
22
+ | Immutable list | IList | module ``pythonic_fp.containers.immutable_list`` |
23
+ +------------------+--------+----------------------------------------------------+
24
+ | Queues | | package ``pythonic_fp.containers.queues`` |
25
+ +------------------+--------+----------------------------------------------------+
26
+
27
+ .. note::
28
+
29
+ Module ``box`` has been moved to ``pythonic_fp.gadgets.box``.
30
+
31
+ """
16
32
 
17
33
  __author__ = 'Geoffrey R. Scheller'
18
34
  __copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
@@ -17,24 +17,29 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  from collections.abc import Callable, Iterator
20
- from typing import cast, Never, overload
21
- from pythonic_fp.iterables import FM, accumulate, concat, exhaust, merge
20
+ from typing import cast, Never, overload, TypeVar
21
+ from pythonic_fp.iterables.folding import accumulate
22
+ from pythonic_fp.iterables.merging import blend, concat, MergeEnum
22
23
 
23
- __all__ = ['FunctionalTuple', 'functional_tuple']
24
+ __all__ = ['FTuple']
24
25
 
25
- class FunctionalTuple[D](tuple[D, ...]):
26
+ D = TypeVar('D', covariant=True)
27
+
28
+ class FTuple[D](tuple[D, ...]):
26
29
  """Functional Tuple suitable for inheritance
27
30
 
28
31
  - Supports both indexing and slicing
29
- - FunctionalTuple addition and int multiplication supported
32
+ - FTuple addition and int multiplication supported
30
33
 
31
34
  - addition concatenates results, resulting in a Union type
32
35
  - both left and right int multiplication supported
33
36
  - homogeneous in its data type
34
37
  - supports being further inherited from
35
38
  - unslotted
36
- """
37
39
 
40
+ - Since these tuples are homogeneous, their covariance may be quirky
41
+
42
+ """
38
43
  def __reversed__(self) -> Iterator[D]:
39
44
  for ii in range(len(self) - 1, -1, -1):
40
45
  yield (self[ii])
@@ -80,19 +85,20 @@ class FunctionalTuple[D](tuple[D, ...]):
80
85
  - fold left with an optional starting value
81
86
  - first argument of function f is for the accumulated value
82
87
 
83
- :raises ValueError: when FunctionalTuple empty and a start value not given
88
+ :raises ValueError: when FTuple empty and a start value not given
89
+
84
90
  """
85
- it = iter(self)
91
+ it_self = iter(self)
86
92
  if start is not None:
87
93
  acc = start
88
94
  elif self:
89
- acc = cast(L, next(it))
95
+ acc = cast(L, next(it_self))
90
96
  else:
91
97
  if default is None:
92
- msg = 'Both start and default cannot be None for an empty FunctionalTuple'
93
- raise ValueError('FunctionalTuple.foldl - ' + msg)
98
+ msg = 'Both start and default cannot be None for an empty FTuple'
99
+ raise ValueError('FTuple.foldl - ' + msg)
94
100
  acc = default
95
- for v in it:
101
+ for v in it_self:
96
102
  acc = f(acc, v)
97
103
  return acc
98
104
 
@@ -108,29 +114,30 @@ class FunctionalTuple[D](tuple[D, ...]):
108
114
  - fold right with an optional starting value
109
115
  - second argument of function f is for the accumulated value
110
116
 
111
- :raises ValueError: when FunctionalTuple empty and a start value not given
117
+ :raises ValueError: when FTuple empty and a start value not given
118
+
112
119
  """
113
- it = reversed(self)
120
+ it_self = reversed(self)
114
121
  if start is not None:
115
122
  acc = start
116
123
  elif self:
117
- acc = cast(R, next(it))
124
+ acc = cast(R, next(it_self))
118
125
  else:
119
126
  if default is None:
120
- msg = 'Both start and default cannot be None for an empty FunctionalTuple'
121
- raise ValueError('FunctionalTuple.foldR - ' + msg)
127
+ msg = 'Both start and default cannot be None for an empty FTuple'
128
+ raise ValueError('FTuple.foldR - ' + msg)
122
129
  acc = default
123
- for v in it:
130
+ for v in it_self:
124
131
  acc = f(v, acc)
125
132
  return acc
126
133
 
127
- def copy(self) -> FunctionalTuple[D]:
128
- """Return a shallow copy of ``FunctionalTuple`` in O(1) time & space complexity."""
134
+ def copy(self) -> FTuple[D]:
135
+ """Return a shallow copy of ``FTuple`` in O(1) time & space complexity."""
129
136
  return self.__class__(self)
130
137
 
131
138
  def __add__(self, other: object, /) -> tuple[D, ...]:
132
- if not isinstance(other, FunctionalTuple):
133
- msg = 'FunctionalTuple being added to something not an FunctionalTuple'
139
+ if not isinstance(other, FTuple):
140
+ msg = 'FTuple being added to something not an FTuple'
134
141
  raise ValueError(msg)
135
142
  return self.__class__(concat(self, other))
136
143
 
@@ -142,48 +149,36 @@ class FunctionalTuple[D](tuple[D, ...]):
142
149
 
143
150
  def accummulate[L](
144
151
  self, f: Callable[[L, D], L], s: L | None = None, /
145
- ) -> FunctionalTuple[L]:
152
+ ) -> FTuple[L]:
146
153
  """Accumulate partial folds
147
154
 
148
- Accumulate partial fold results in an ``FunctionalTuple`` with an optional
155
+ Accumulate partial fold results in an ``FTuple`` with an optional
149
156
  starting value.
157
+
150
158
  """
151
159
  if s is None:
152
- return FunctionalTuple(accumulate(self, f))
153
- return FunctionalTuple(accumulate(self, f, s))
160
+ return FTuple(accumulate(self, f))
161
+ return FTuple(accumulate(self, f, s))
154
162
 
155
- def map[U](self, f: Callable[[D], U], /) -> FunctionalTuple[U]:
156
- return FunctionalTuple(map(f, self))
163
+ def map[U](self, f: Callable[[D], U], /) -> FTuple[U]:
164
+ return FTuple(map(f, self))
157
165
 
158
166
  def bind[U](
159
- self, f: Callable[[D], FunctionalTuple[U]], type: FM = FM.CONCAT, /
160
- ) -> FunctionalTuple[U] | Never:
161
- """Bind function ``f`` to the ``FunctionalTuple``.
167
+ self, f: Callable[[D], FTuple[U]],
168
+ merge_enum: MergeEnum = MergeEnum.Concat,
169
+ yield_partials: bool = False,
170
+ ) -> FTuple[U] | Never:
171
+ """Bind function ``f`` to the ``FTuple``.
162
172
 
163
- - FM Enum types
173
+ :param ds: values to instantiate FTuple
174
+ :return: resulting FTuple
175
+ :raises ValueError: if given unknown merge_type
164
176
 
165
- - CONCAT: sequentially concatenate iterables one after the other
166
- - MERGE: round-robin merge iterables until one is exhausted
167
- - EXHAUST: round-robin merge iterables until all are exhausted
168
-
169
- :param ds: values to instantiate FunctionalTuple
170
- :return: resulting FunctionalTuple
171
177
  """
172
- match type:
173
- case FM.CONCAT:
174
- return FunctionalTuple(concat(*map(f, self)))
175
- case FM.MERGE:
176
- return FunctionalTuple(merge(*map(f, self)))
177
- case FM.EXHAUST:
178
- return FunctionalTuple(exhaust(*map(f, self)))
179
-
180
- raise ValueError('Unknown FM type')
181
-
182
-
183
- def functional_tuple[D](*ds: D):
184
- """Construct a ``FunctionalTuple`` from arguments.
185
-
186
- :param ds: values to instantiate FunctionalTuple
187
- :return: resulting FunctionalTuple
188
- """
189
- return FunctionalTuple(ds)
178
+ return FTuple(
179
+ blend(
180
+ *map(f, self),
181
+ merge_enum=merge_enum,
182
+ yield_partials=yield_partials
183
+ )
184
+ )
@@ -18,8 +18,8 @@
18
18
  - declared covariant in its generic datatype
19
19
  - hashability should be enforced by LSP tooling
20
20
  - hashability will be enforced at runtime
21
- - ImmutableList addition supported via concatenation
22
- - ImmutableList integer multiplication supported
21
+ - IList addition supported via concatenation
22
+ - IList integer multiplication supported
23
23
 
24
24
  """
25
25
 
@@ -27,14 +27,15 @@ from __future__ import annotations
27
27
 
28
28
  from collections.abc import Callable, Iterable, Iterator, Hashable
29
29
  from typing import cast, Never, overload, TypeVar
30
- from pythonic_fp.iterables import FM, accumulate, concat, exhaust, merge
30
+ from pythonic_fp.iterables.folding import accumulate
31
+ from pythonic_fp.iterables.merging import blend, concat, MergeEnum
31
32
 
32
- __all__ = ['ImmutableList', 'immutable_list']
33
+ __all__ = ['IList']
33
34
 
34
35
  D = TypeVar('D', covariant=True)
35
36
  T = TypeVar('T')
36
37
 
37
- class ImmutableList[D](Hashable):
38
+ class IList[D](Hashable):
38
39
  """Immutable List like data structure.
39
40
 
40
41
  - its method type parameters are also covariant
@@ -42,8 +43,8 @@ class ImmutableList[D](Hashable):
42
43
  - supports both indexing and slicing
43
44
  - addition concatenates results, resulting type a Union type
44
45
  - both left and right int multiplication supported
45
- """
46
46
 
47
+ """
47
48
  __slots__ = ('_ds', '_len', '_hash')
48
49
  __match_args__ = ('_ds', '_len')
49
50
 
@@ -53,7 +54,7 @@ class ImmutableList[D](Hashable):
53
54
 
54
55
  def __init__(self, *dss: Iterable[D]) -> None:
55
56
  if (size := len(dss)) > 1:
56
- msg = f'ImmutableList expects at most 1 iterable argument, got {size}'
57
+ msg = f'IList expects at most 1 iterable argument, got {size}'
57
58
  raise ValueError(msg)
58
59
  else:
59
60
  self._ds: tuple[D, ...] = tuple(dss[0]) if size == 1 else tuple()
@@ -61,7 +62,7 @@ class ImmutableList[D](Hashable):
61
62
  try:
62
63
  self._hash = hash((self._len, 42) + self._ds)
63
64
  except TypeError as exc:
64
- msg = f'ImmutableList: {exc}'
65
+ msg = f'IList: {exc}'
65
66
  raise TypeError(msg)
66
67
 
67
68
  def __hash__(self) -> int:
@@ -86,7 +87,7 @@ class ImmutableList[D](Hashable):
86
87
  return '((' + ', '.join(map(repr, self)) + '))'
87
88
 
88
89
  def __eq__(self, other: object, /) -> bool:
89
- if not isinstance(other, ImmutableList):
90
+ if not isinstance(other, IList):
90
91
  return NotImplemented # magic object
91
92
  if self._len != other._len:
92
93
  return False
@@ -97,11 +98,11 @@ class ImmutableList[D](Hashable):
97
98
  @overload
98
99
  def __getitem__(self, idx: int, /) -> D: ...
99
100
  @overload
100
- def __getitem__(self, idx: slice, /) -> ImmutableList[D]: ...
101
+ def __getitem__(self, idx: slice, /) -> IList[D]: ...
101
102
 
102
- def __getitem__(self, idx: slice | int, /) -> ImmutableList[D] | D:
103
+ def __getitem__(self, idx: slice | int, /) -> IList[D] | D:
103
104
  if isinstance(idx, slice):
104
- return ImmutableList(self._ds[idx])
105
+ return IList(self._ds[idx])
105
106
  return self._ds[idx]
106
107
 
107
108
  def foldl[L](
@@ -117,6 +118,7 @@ class ImmutableList[D](Hashable):
117
118
  - first argument of function ``f`` is for the accumulated value
118
119
 
119
120
  "raises ValueError: when empty and a start value not given
121
+
120
122
  """
121
123
  it = iter(self._ds)
122
124
  if start is not None:
@@ -125,9 +127,9 @@ class ImmutableList[D](Hashable):
125
127
  acc = cast(L, next(it)) # L_co = D_co in this case
126
128
  else:
127
129
  if default is None:
128
- msg0 = 'ImmutableList: foldl method requires '
130
+ msg0 = 'IList: foldl method requires '
129
131
  msg1 = 'either start or default to be defined for '
130
- msg2 = 'an empty ImmutableList'
132
+ msg2 = 'an empty IList'
131
133
  raise ValueError(msg0 + msg1 + msg2)
132
134
  acc = default
133
135
  for v in it:
@@ -147,6 +149,7 @@ class ImmutableList[D](Hashable):
147
149
  - second argument of function ``f`` is for the accumulated value
148
150
 
149
151
  "raises ValueError: when empty and a start value not given
152
+
150
153
  """
151
154
  it = reversed(self._ds)
152
155
  if start is not None:
@@ -155,68 +158,61 @@ class ImmutableList[D](Hashable):
155
158
  acc = cast(R, next(it))
156
159
  else:
157
160
  if default is None:
158
- msg0 = 'ImmutableList: foldr method requires '
161
+ msg0 = 'IList: foldr method requires '
159
162
  msg1 = 'either start or default to be defined for '
160
- msg2 = 'an empty ImmutableList'
163
+ msg2 = 'an empty IList'
161
164
  raise ValueError(msg0 + msg1 + msg2)
162
165
  acc = default
163
166
  for v in it:
164
167
  acc = f(v, acc)
165
168
  return acc
166
169
 
167
- def __add__(self, other: ImmutableList[D], /) -> ImmutableList[D]:
168
- if not isinstance(other, ImmutableList):
169
- msg = 'ImmutableList being added to something not a ImmutableList'
170
+ def __add__(self, other: IList[D], /) -> IList[D]:
171
+ if not isinstance(other, IList):
172
+ msg = 'IList being added to something not a IList'
170
173
  raise ValueError(msg)
171
174
 
172
- return ImmutableList(concat(self, other))
175
+ return IList(concat(self, other))
173
176
 
174
- def __mul__(self, num: int, /) -> ImmutableList[D]:
175
- return ImmutableList(self._ds.__mul__(num if num > 0 else 0))
177
+ def __mul__(self, num: int, /) -> IList[D]:
178
+ return IList(self._ds.__mul__(num if num > 0 else 0))
176
179
 
177
- def __rmul__(self, num: int, /) -> ImmutableList[D]:
178
- return ImmutableList(self._ds.__mul__(num if num > 0 else 0))
180
+ def __rmul__(self, num: int, /) -> IList[D]:
181
+ return IList(self._ds.__mul__(num if num > 0 else 0))
179
182
 
180
183
  def accummulate[L](
181
184
  self, f: Callable[[L, D], L], s: L | None = None, /
182
- ) -> ImmutableList[L]:
185
+ ) -> IList[L]:
183
186
  """Accumulate partial folds
184
187
 
185
- Accumulate partial fold results in an ImmutableList with
188
+ Accumulate partial fold results in an IList with
186
189
  an optional starting value.
190
+
187
191
  """
188
192
  if s is None:
189
- return ImmutableList(accumulate(self, f))
190
- return ImmutableList(accumulate(self, f, s))
193
+ return IList(accumulate(self, f))
194
+ return IList(accumulate(self, f, s))
191
195
 
192
- def map[U](self, f: Callable[[D], U], /) -> ImmutableList[U]:
193
- return ImmutableList(map(f, self))
196
+ def map[U](self, f: Callable[[D], U], /) -> IList[U]:
197
+ return IList(map(f, self))
194
198
 
195
199
  def bind[U](
196
- self, f: Callable[[D], ImmutableList[U]], type: FM = FM.CONCAT, /
197
- ) -> ImmutableList[U] | Never:
198
- """Bind function `f` to the `ImmutableList`.
200
+ self,
201
+ f: Callable[[D], IList[U]],
202
+ merge_enum: MergeEnum = MergeEnum.Concat,
203
+ yield_partials: bool = False,
204
+ ) -> IList[U] | Never:
205
+ """Bind function `f` to the `IList`.
199
206
 
200
- - FM Enum types
207
+ :param ds: values to instantiate IList
208
+ :return: resulting IList
209
+ :raises ValueError: if given unknown merge_type
201
210
 
202
- - CONCAT: sequentially concatenate iterables one after the other
203
- - MERGE: round-robin merge iterables until one is exhausted
204
- - EXHAUST: round-robin merge iterables until all are exhausted
205
211
  """
206
- match type:
207
- case FM.CONCAT:
208
- return ImmutableList(concat(*map(f, self)))
209
- case FM.MERGE:
210
- return ImmutableList(merge(*map(f, self)))
211
- case FM.EXHAUST:
212
- return ImmutableList(exhaust(*map(f, self)))
213
-
214
- raise ValueError(f'ImmutableList: Unknown FM type: {type}')
215
-
216
-
217
- def immutable_list[T](*ts: T) -> ImmutableList[TabError]:
218
- """Function to produce an ``ImmutableList`` from a variable number of arguments.
219
-
220
- :param ds: initial values to push onto a new ImmutableList from right to left
221
- """
222
- return ImmutableList(ts)
212
+ return IList(
213
+ blend(
214
+ *map(f, self),
215
+ merge_enum=merge_enum,
216
+ yield_partials=yield_partials
217
+ )
218
+ )
@@ -0,0 +1,31 @@
1
+ # Copyright 2023-2024 Geoffrey R. Scheller
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Three types of queues.
16
+
17
+ +------------------------------------+-----------+--------------------------+
18
+ | module | Class | description |
19
+ +====================================+===========+==========================+
20
+ | pythonic_fp.containers.queues.fifo | FIFOQueue | First-In-First-Out queue |
21
+ +------------------------------------+-----------+--------------------------+
22
+ | pythonic_fp.containers.queues.lifo | LIFOQueue | Last-In-First-Out queue |
23
+ +------------------------------------+-----------+--------------------------+
24
+ | pythonic_fp.containers.queues.de | DEQueue | Double-Ended queue |
25
+ +------------------------------------+-----------+--------------------------+
26
+
27
+ """
28
+
29
+ __author__ = 'Geoffrey R. Scheller'
30
+ __copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
31
+ __license__ = 'Apache License 2.0'
@@ -0,0 +1,180 @@
1
+ # Copyright 2023-2025 Geoffrey R. Scheller
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Stateful Double-Ended (DE) Queue data structure.
16
+
17
+ - O(1) pops each end
18
+ - O(1) amortized pushes each end
19
+ - O(1) length determination
20
+ - will automatically increase storage capacity when needed
21
+ - in a Boolean context, true if not empty, false if empty
22
+ - neither indexable nor sliceable by design
23
+
24
+ """
25
+ from __future__ import annotations
26
+
27
+ from collections.abc import Callable, Iterable, Iterator
28
+ from typing import TypeVar
29
+ from pythonic_fp.circulararray.auto import CA
30
+ from pythonic_fp.fptools.maybe import MayBe
31
+
32
+ __all__ = ['DEQueue', 'de_queue']
33
+
34
+ D = TypeVar('D')
35
+
36
+
37
+ class DEQueue[D]:
38
+
39
+ __slots__ = ('_ca',)
40
+
41
+ def __init__(self, *dss: Iterable[D]) -> None:
42
+ """Initial data in FIFO order, newest to oldest, as if
43
+ pushed on from the right side.
44
+
45
+ :param dss: takes up to one iterable
46
+ :raises ValueError: if more than 1 iterable is given
47
+
48
+ """
49
+ if (size := len(dss)) > 1:
50
+ msg = f'DEQueue expects at most 1 argument, got {size}'
51
+ raise ValueError(msg)
52
+ self._ca = CA(dss[0]) if size == 1 else CA()
53
+
54
+ def __bool__(self) -> bool:
55
+ return len(self._ca) > 0
56
+
57
+ def __len__(self) -> int:
58
+ return len(self._ca)
59
+
60
+ def __eq__(self, other: object) -> bool:
61
+ if not isinstance(other, DEQueue):
62
+ return False
63
+ return self._ca == other._ca
64
+
65
+ def __iter__(self) -> Iterator[D]:
66
+ return iter(list(self._ca))
67
+
68
+ def __reversed__(self) -> Iterator[D]:
69
+ return reversed(list(self._ca))
70
+
71
+ def __repr__(self) -> str:
72
+ if len(self) == 0:
73
+ return 'DEQueue()'
74
+ return 'DEQueue(' + ', '.join(map(repr, self._ca)) + ')'
75
+
76
+ def __str__(self) -> str:
77
+ return '>< ' + ' | '.join(map(str, self)) + ' ><'
78
+
79
+ def copy(self) -> DEQueue[D]:
80
+ """Shallow copy.
81
+
82
+ :return: shallow copy of the DEQueue
83
+
84
+ """
85
+ return DEQueue(self._ca)
86
+
87
+ def pushl(self, *ds: D) -> None:
88
+ """Push data onto left side of DEQueue.
89
+
90
+ :param ds: items to be pushed onto DEQueue from the left
91
+
92
+ """
93
+ self._ca.pushl(*ds)
94
+
95
+ def pushr(self, *ds: D) -> None:
96
+ """Push data onto right side of DEQueue.
97
+
98
+ :param ds: items to be pushed onto DEQueue from the right
99
+
100
+ """
101
+ self._ca.pushr(*ds)
102
+
103
+ def popl(self) -> MayBe[D]:
104
+ """Pop next data item from left side DEQueue, if it exists.
105
+
106
+ :return: MayBe of popped item if queue was not empty, empty MayBe otherwise
107
+
108
+ """
109
+ if self._ca:
110
+ return MayBe(self._ca.popl())
111
+ return MayBe()
112
+
113
+ def popr(self) -> MayBe[D]:
114
+ """Pop next item off right side DEQueue.
115
+
116
+ :return: MayBe of popped item if queue was not empty, empty MayBe otherwise
117
+
118
+ """
119
+ if self._ca:
120
+ return MayBe(self._ca.popr())
121
+ return MayBe()
122
+
123
+ def peakl(self) -> MayBe[D]:
124
+ """Peak at data on left side of DEQueue.
125
+
126
+ :return: MayBe of leftmost data if queue not empty, empty MayBe otherwise
127
+
128
+ """
129
+ if self._ca:
130
+ return MayBe(self._ca[0])
131
+ return MayBe()
132
+
133
+ def peakr(self) -> MayBe[D]:
134
+ """Peak at right side of DEQueue. Does not consume item.
135
+
136
+ :return: MayBe of rightmost data if queue not empty, empty MayBe otherwise
137
+
138
+ """
139
+ if self._ca:
140
+ return MayBe(self._ca[-1])
141
+ return MayBe()
142
+
143
+ def foldl[L](self, f: Callable[[L, D], L], start: L | None = None) -> MayBe[L]:
144
+ """Reduces DEQueue left to right.
145
+
146
+ :param f: reducing function, first argument is for accumulator
147
+ :param start: optional starting value
148
+ :return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
149
+
150
+ """
151
+ if start is None:
152
+ if not self._ca:
153
+ return MayBe()
154
+ return MayBe(self._ca.foldl(f, start))
155
+
156
+ def foldr[R](self, f: Callable[[D, R], R], start: R | None = None) -> MayBe[R]:
157
+ """Reduces DEQueue right to left.
158
+
159
+ :param f: reducing function, second argument is for accumulator
160
+ :param start: optional starting value
161
+ :return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
162
+
163
+ """
164
+ if start is None:
165
+ if not self._ca:
166
+ return MayBe()
167
+ return MayBe(self._ca.foldr(f, start))
168
+
169
+ def map[U](self, f: Callable[[D], U]) -> DEQueue[U]:
170
+ """Map f over the DEQueue left to right, retain original order.
171
+
172
+ :return: new DEQueue instance
173
+
174
+ """
175
+ return DEQueue(map(f, self._ca))
176
+
177
+
178
+ def de_queue[D](*ds: D) -> DEQueue[D]:
179
+ """DEQueue factory function."""
180
+ return DEQueue(ds)