funstruct 0.1.0__tar.gz → 0.1.2__tar.gz

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.
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: funstruct
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: fun & functional structures
5
+ Home-page: https://github.com/veyga/funstruct
5
6
  License: MIT
6
- Keywords: functional,immutable,fp
7
+ Keywords: functional,immutable,fp,data structures,cons
7
8
  Author: Andrew Stefanich
8
9
  Author-email: andrewstefanich@gmail.com
9
10
  Requires-Python: >=3.12,<4.0
@@ -12,6 +13,7 @@ Classifier: Programming Language :: Python :: 3
12
13
  Classifier: Programming Language :: Python :: 3.12
13
14
  Requires-Dist: pytest-parametrization (>=2022.2.1,<2023.0.0)
14
15
  Requires-Dist: returns (>=0.23.0,<0.24.0)
16
+ Project-URL: Repository, https://github.com/veyga/funstruct
15
17
  Description-Content-Type: text/markdown
16
18
 
17
19
  # funstruct
File without changes
@@ -1,11 +1,13 @@
1
1
  [tool.poetry]
2
2
  name = "funstruct"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "fun & functional structures"
5
5
  authors = ["Andrew Stefanich <andrewstefanich@gmail.com>"]
6
6
  license = "MIT"
7
7
  readme = "README.md"
8
- keywords = ["functional", "immutable", "fp"]
8
+ homepage = "https://github.com/veyga/funstruct"
9
+ repository = "https://github.com/veyga/funstruct"
10
+ keywords = ["functional", "immutable", "fp", "data structures", "cons"]
9
11
 
10
12
  [tool.poetry.dependencies]
11
13
  python = "^3.12"
@@ -18,6 +20,7 @@ pytest = "^8.3.2"
18
20
  debugpy = "^1.8.2"
19
21
  black = "^24.4.2"
20
22
  pre-commit = "^3.7.1"
23
+ sphinx = "^7.4.7"
21
24
 
22
25
  [build-system]
23
26
  requires = ["poetry-core"]
@@ -1,2 +0,0 @@
1
- from funstruct._frozendict import frozendict
2
- from funstruct._cons import CList, Cons, Nil
@@ -1,17 +0,0 @@
1
- from dataclasses import dataclass
2
-
3
-
4
- @dataclass
5
- class Nil2_: ...
6
-
7
-
8
- Nil2 = Nil2_()
9
-
10
-
11
- @dataclass
12
- class Cons2[A]:
13
- head: A
14
- tail: "Cons2[A]"
15
-
16
- def __repr__(self):
17
- return f"Cons({self.head}, {self.tail})"
@@ -1,223 +0,0 @@
1
- """
2
- A Lisp/ML/Scala style singly linked list ("cons list")
3
- """
4
-
5
- from abc import abstractmethod, ABC
6
- from dataclasses import dataclass, field
7
- from typing import Callable, Iterable, Tuple
8
- from typing import TypeVar
9
-
10
-
11
- A = TypeVar("A")
12
-
13
- type _CList = "CList[A]"
14
-
15
-
16
- class CList[A](ABC):
17
-
18
- @abstractmethod
19
- def append(self, other: _CList) -> _CList: ...
20
-
21
- @abstractmethod
22
- def fold_right[B](self, acc: B, f: Callable[[A, B], B]) -> B: ...
23
-
24
- @abstractmethod
25
- def fold_left[B](self, acc: B, f: Callable[[B, A], B]) -> B: ...
26
-
27
- @abstractmethod
28
- def drop(self, n: int) -> _CList: ...
29
-
30
- @abstractmethod
31
- def drop_while(self, f: Callable[[A], bool]) -> _CList: ...
32
-
33
- @abstractmethod
34
- def take(self, n: int) -> _CList: ...
35
-
36
- @abstractmethod
37
- def take_while(self, f: Callable[[A], bool]) -> _CList: ...
38
-
39
- @abstractmethod
40
- def split_at(self, i: int) -> Tuple[_CList, _CList]: ...
41
-
42
- def partition(self, f: Callable[[A], bool]) -> Tuple[_CList, _CList]:
43
- accum = lambda a, x: (a << x[0], x[1]) if f(a) else (x[0], a << x[1])
44
- return self.fold_right((Nil(), Nil()), accum)
45
-
46
- def length(self) -> int:
47
- return self.fold_right(0, lambda _, acc: acc + 1)
48
-
49
- def prepend(self, new_head: A) -> _CList:
50
- return Cons(new_head, self)
51
-
52
- def reversed(self) -> _CList:
53
- return self.fold_left(Nil(), lambda acc, h: Cons(h, acc))
54
-
55
- def map[B](self, f: Callable[[A], B]) -> "CList[B]":
56
- return self.fold_right(Nil(), lambda a, acc: Cons(f(a), acc))
57
-
58
- def filter[A](self, f: Callable[[A], bool]) -> _CList:
59
- return self.fold_right(Nil(), lambda a, acc: Cons(a, acc) if f(a) else acc)
60
-
61
- def flatten(self) -> _CList:
62
- return CList.flatten_(self)
63
-
64
- def flat_map[B](self, f: Callable[[A], "CList[B]"]) -> "CList[B]":
65
- return self.map(f).flatten()
66
-
67
- def bind[B](self, f: Callable[[A], "CList[B]"]) -> "CList[B]":
68
- return self.map(f).flatten()
69
-
70
- def sorted(self, cmp: Callable[[A, A], int]) -> _CList:
71
- def merge(left: _CList, right: _CList) -> _CList:
72
- match left, right:
73
- case Nil(), r:
74
- return r
75
- case l, Nil():
76
- return l
77
- case Cons(lh, lt), Cons(rh, rt):
78
- if cmp(lh, rh) <= 0:
79
- return lh << merge(lt, right)
80
- return rh << merge(left, rt)
81
- case _:
82
- return Nil()
83
-
84
- length = len(self)
85
- if length <= 1:
86
- return self
87
- left, right = self.split_at(length // 2)
88
- return merge(left.sorted(cmp), right.sorted(cmp))
89
-
90
- @staticmethod
91
- def flatten_(lst: "CList[CList[A]]") -> _CList:
92
- def concat(left, right):
93
- match left:
94
- case Nil():
95
- return right
96
- case Cons(h, t):
97
- return Cons(h, concat(t, right))
98
-
99
- def flatten(lst: "CList[CList[A]]") -> "CList[A]":
100
- match lst:
101
- case Nil():
102
- return Nil()
103
- case Cons(h, t):
104
- match h:
105
- case Cons(_, _):
106
- return concat(flatten(h), flatten(t))
107
- case _:
108
- return Cons(h, flatten(t))
109
-
110
- return flatten(lst)
111
-
112
- @staticmethod
113
- def cons(a: A) -> _CList:
114
- return Cons(a)
115
-
116
- @staticmethod
117
- def empty() -> _CList:
118
- return Nil()
119
-
120
- @staticmethod
121
- def new(*xs: A) -> _CList:
122
- return Cons(xs[0], CList.new(*xs[1:])) if xs else Nil()
123
-
124
- @staticmethod
125
- def from_iterable[A](iterable: Iterable[A]) -> _CList:
126
- return CList.new(*iterable)
127
-
128
- def __rlshift__(self, other) -> _CList:
129
- return self.prepend(other)
130
-
131
- def __add__(self, other) -> _CList:
132
- return self.append(other)
133
-
134
- def __len__(self) -> int:
135
- return self.fold_right(0, lambda _, acc: acc + 1)
136
-
137
- def __iter__(self):
138
- current = self
139
- while isinstance(current, Cons):
140
- yield current.head
141
- current = current.tail
142
-
143
- def __eq__(self, other) -> bool:
144
- match self, other:
145
- case Cons(sh, st), Cons(oh, ot):
146
- return sh == oh and st == ot
147
- case Nil(), Nil():
148
- return True
149
- case _:
150
- return False
151
-
152
-
153
- class Nil(CList):
154
-
155
- _instance = None
156
-
157
- def __new__(cls):
158
- if cls._instance is None:
159
- cls._instance = super(Nil, cls).__new__(cls)
160
- return cls._instance
161
-
162
- def __repr__(self):
163
- return "Nil()"
164
-
165
- def append(self, other: _CList) -> _CList:
166
- return other
167
-
168
- def fold_right[A, B](self, acc: B, f: Callable[[A, B], B]) -> B:
169
- return acc
170
-
171
- def fold_left[A, B](self, acc: B, f: Callable[[B, A], B]) -> B:
172
- return acc
173
-
174
- def drop(self, n: int) -> _CList:
175
- return self
176
-
177
- def drop_while[A](self, f: Callable[[A], bool]) -> _CList:
178
- return self
179
-
180
- def take(self, n: int) -> _CList:
181
- return self
182
-
183
- def take_while[A](self, f: Callable[[A], bool]) -> _CList:
184
- return self
185
-
186
- def split_at(self, i: int) -> Tuple[_CList, _CList]:
187
- return self, self
188
-
189
-
190
- @dataclass(frozen=True)
191
- class Cons[A](CList[A]):
192
- head: A
193
- tail: CList[A] = field(default_factory=Nil)
194
-
195
- def __repr__(self):
196
- match self.tail:
197
- case Nil():
198
- return f"Cons({self.head})"
199
- return f"Cons({self.head}, {self.tail})"
200
-
201
- def append(self, other: _CList) -> _CList:
202
- return Cons(self.head, self.tail.append(other))
203
-
204
- def fold_right[B](self, acc: B, f: Callable[[A, B], B]) -> B:
205
- return f(self.head, self.tail.fold_right(acc, f))
206
-
207
- def fold_left[B](self, acc: B, f: Callable[[B, A], B]) -> B:
208
- return self.tail.fold_left(f(acc, self.head), f)
209
-
210
- def drop(self, n: int) -> _CList:
211
- return self if n <= 0 else self.tail.drop(n - 1)
212
-
213
- def drop_while(self, f: Callable[[A], bool]) -> _CList:
214
- return self if not f(self.head) else self.tail.drop_while(f)
215
-
216
- def take(self, n: int) -> _CList:
217
- return Cons(self.head) if n <= 1 else self.head << self.tail.take(n - 1)
218
-
219
- def take_while(self, f: Callable[[A], bool]) -> _CList:
220
- return self.head << self.tail.take_while(f) if f(self.head) else Nil()
221
-
222
- def split_at(self, i: int) -> Tuple[_CList, _CList]:
223
- return self.take(i), self.drop(i)
File without changes
File without changes