iterativerecursion 0.1__tar.gz → 1.0__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.
- iterativerecursion-1.0/LICENSE +21 -0
- iterativerecursion-1.0/PKG-INFO +399 -0
- iterativerecursion-1.0/README.md +376 -0
- iterativerecursion-1.0/iterativerecursion/iterativerecursion.py +223 -0
- iterativerecursion-1.0/iterativerecursion.egg-info/PKG-INFO +399 -0
- {iterativerecursion-0.1 → iterativerecursion-1.0}/iterativerecursion.egg-info/SOURCES.txt +5 -2
- iterativerecursion-1.0/iterativerecursion.egg-info/requires.txt +3 -0
- iterativerecursion-1.0/pyproject.toml +37 -0
- iterativerecursion-1.0/tests/test_iterativerecursion.py +572 -0
- iterativerecursion-0.1/PKG-INFO +0 -122
- iterativerecursion-0.1/README.md +0 -101
- iterativerecursion-0.1/iterativerecursion/iterativerecursion.py +0 -100
- iterativerecursion-0.1/iterativerecursion.egg-info/PKG-INFO +0 -122
- iterativerecursion-0.1/setup.py +0 -33
- {iterativerecursion-0.1 → iterativerecursion-1.0}/iterativerecursion/__init__.py +0 -0
- {iterativerecursion-0.1 → iterativerecursion-1.0}/iterativerecursion.egg-info/dependency_links.txt +0 -0
- {iterativerecursion-0.1 → iterativerecursion-1.0}/iterativerecursion.egg-info/top_level.txt +0 -0
- {iterativerecursion-0.1 → iterativerecursion-1.0}/setup.cfg +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Carlos A. Planchón
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iterativerecursion
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: A Python module to simulate recursive function calls using iteration, providing explicit control over execution flow and avoiding stack overflow issues.
|
|
5
|
+
Author-email: "Carlos A. Planchón" <carlosandresplanchonprestes@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/carlosplanchon/iterativerecursion
|
|
8
|
+
Keywords: iterative,recursion
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# iterativerecursion
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
*A Python module to simulate recursive function calls using iteration, providing explicit control over execution flow and avoiding stack overflow issues.*
|
|
29
|
+
|
|
30
|
+
## Overview
|
|
31
|
+
|
|
32
|
+
`iterativerecursion` provides a mechanism to chain function calls iteratively while maintaining a recursive-like pattern. Instead of relying on the call stack, functions explicitly declare what to call next, making the execution flow transparent and controllable.
|
|
33
|
+
|
|
34
|
+
### Why Use This?
|
|
35
|
+
|
|
36
|
+
- **Avoid Stack Overflow**: Handle deep recursion without hitting Python's recursion limit
|
|
37
|
+
- **Explicit Control**: See and control the exact flow of function calls
|
|
38
|
+
- **Debugging**: Easier to trace execution without deep call stacks
|
|
39
|
+
- **State Management**: Shared environment for passing data between functions
|
|
40
|
+
- **Safety**: Built-in iteration limits to prevent infinite loops
|
|
41
|
+
|
|
42
|
+
### When to Use This
|
|
43
|
+
|
|
44
|
+
This library is useful when you need:
|
|
45
|
+
- Deep recursion that exceeds Python's stack limit (~1000 calls)
|
|
46
|
+
- Explicit control over recursive execution flow
|
|
47
|
+
- To convert recursive algorithms to iterative ones systematically
|
|
48
|
+
- State machines or complex control flow patterns
|
|
49
|
+
|
|
50
|
+
## Scope & limitations
|
|
51
|
+
|
|
52
|
+
This engine is essentially a **trampoline / dispatcher**: each step returns the name of the next function to run.
|
|
53
|
+
That makes it a great fit for **tail recursion (linear step-by-step recursion)**, workflows, and state machines.
|
|
54
|
+
|
|
55
|
+
It does **not** automatically emulate a full call stack. Patterns that require “returning to the caller”
|
|
56
|
+
(e.g. tree recursion like `fib(n-1) + fib(n-2)`, post-order DFS reductions) require you to model an explicit
|
|
57
|
+
stack/frames (continuations) inside `environment_variables`.
|
|
58
|
+
|
|
59
|
+
For most cases, **normal recursion is simpler and preferred**. Use this when recursion depth or explicit control becomes a concern.
|
|
60
|
+
|
|
61
|
+
## Documentation
|
|
62
|
+
|
|
63
|
+
📚 **[View interactive documentation on DeepWiki](https://deepwiki.com/carlosplanchon/iterativerecursion)**
|
|
64
|
+
|
|
65
|
+
Explore the full API reference, examples, and guides in an interactive format.
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
### Using uv
|
|
70
|
+
```bash
|
|
71
|
+
uv add iterativerecursion
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Using pip
|
|
75
|
+
```bash
|
|
76
|
+
pip install iterativerecursion
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
|
|
83
|
+
|
|
84
|
+
def greet(name: str) -> FunctionReturn:
|
|
85
|
+
print(f"Hello, {name}!")
|
|
86
|
+
return FunctionReturn(
|
|
87
|
+
returned_values={"next_name": "World"},
|
|
88
|
+
next_function_to_call="farewell",
|
|
89
|
+
arg_env_mapping={"name": "next_name"}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def farewell(name: str) -> FunctionReturn:
|
|
93
|
+
print(f"Goodbye, {name}!")
|
|
94
|
+
return FunctionReturn(
|
|
95
|
+
returned_values={}
|
|
96
|
+
# next_function_to_call defaults to None to terminate
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Create engine and register functions
|
|
100
|
+
engine = IterativeRecursionEngine()
|
|
101
|
+
engine.add_function(greet)
|
|
102
|
+
engine.add_function(farewell)
|
|
103
|
+
|
|
104
|
+
# Start execution
|
|
105
|
+
engine.start_function_caller(
|
|
106
|
+
next_function_to_call="greet",
|
|
107
|
+
environment_variables={"initial_name": "Alice"},
|
|
108
|
+
arg_env_mapping={"name": "initial_name"}
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Output:**
|
|
113
|
+
```
|
|
114
|
+
Hello, Alice!
|
|
115
|
+
Goodbye, World!
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## How It Works
|
|
119
|
+
|
|
120
|
+
Functions return a `FunctionReturn` dataclass instance with three attributes:
|
|
121
|
+
|
|
122
|
+
| Attribute | Type | Description |
|
|
123
|
+
|-----------|------|-------------|
|
|
124
|
+
| `returned_values` | `dict[str, Any]` | Values to store in the shared environment |
|
|
125
|
+
| `next_function_to_call` | `str \| None` | Name of the next function to execute, or `None` to stop (default: `None`) |
|
|
126
|
+
| `arg_env_mapping` | `dict[str, str]` | Mapping of parameter names to environment variable keys (default: auto-mapped from `returned_values` keys) |
|
|
127
|
+
|
|
128
|
+
**Key Feature**: If `arg_env_mapping` is not specified, it automatically maps each key in `returned_values` to itself. For example, `{"counter": 5}` automatically creates `{"counter": "counter"}` mapping.
|
|
129
|
+
|
|
130
|
+
The engine maintains a shared environment where functions can store and retrieve values across calls.
|
|
131
|
+
|
|
132
|
+
## Examples
|
|
133
|
+
|
|
134
|
+
### Using the @register Decorator
|
|
135
|
+
|
|
136
|
+
The `@register` decorator provides a cleaner way to register functions:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
|
|
140
|
+
|
|
141
|
+
engine = IterativeRecursionEngine()
|
|
142
|
+
|
|
143
|
+
@engine.register
|
|
144
|
+
def greet(name: str) -> FunctionReturn:
|
|
145
|
+
print(f"Hello, {name}!")
|
|
146
|
+
return FunctionReturn(
|
|
147
|
+
returned_values={"next_name": "World"},
|
|
148
|
+
next_function_to_call="farewell",
|
|
149
|
+
arg_env_mapping={"name": "next_name"}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
@engine.register
|
|
153
|
+
def farewell(name: str) -> FunctionReturn:
|
|
154
|
+
print(f"Goodbye, {name}!")
|
|
155
|
+
return FunctionReturn(
|
|
156
|
+
returned_values={}
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Start execution and get final state
|
|
160
|
+
result = engine.start_function_caller(
|
|
161
|
+
next_function_to_call="greet",
|
|
162
|
+
environment_variables={"initial_name": "Alice"},
|
|
163
|
+
arg_env_mapping={"name": "initial_name"}
|
|
164
|
+
)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Factorial Calculation
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
|
|
171
|
+
|
|
172
|
+
def factorial_step(n: int, accumulator: int) -> FunctionReturn:
|
|
173
|
+
if n <= 1:
|
|
174
|
+
return FunctionReturn(
|
|
175
|
+
returned_values={"result": accumulator}
|
|
176
|
+
)
|
|
177
|
+
# Auto-mapping: {"n": n-1, "accumulator": ...} automatically maps to itself
|
|
178
|
+
return FunctionReturn(
|
|
179
|
+
returned_values={
|
|
180
|
+
"n": n - 1,
|
|
181
|
+
"accumulator": accumulator * n
|
|
182
|
+
},
|
|
183
|
+
next_function_to_call="factorial_step"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
engine = IterativeRecursionEngine()
|
|
187
|
+
engine.add_function(factorial_step)
|
|
188
|
+
engine.start_function_caller(
|
|
189
|
+
next_function_to_call="factorial_step",
|
|
190
|
+
environment_variables={"n": 5, "accumulator": 1},
|
|
191
|
+
arg_env_mapping={"n": "n", "accumulator": "accumulator"}
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
print(f"5! = {engine.environment_variables['result']}") # Output: 5! = 120
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Fibonacci Sequence (tail-recursive / iterative form)
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
|
|
201
|
+
|
|
202
|
+
def fibonacci(n: int, a: int, b: int) -> FunctionReturn:
|
|
203
|
+
if n == 0:
|
|
204
|
+
return FunctionReturn(
|
|
205
|
+
returned_values={"result": a}
|
|
206
|
+
)
|
|
207
|
+
# Auto-mapping handles {"n": "n", "a": "a", "b": "b"} automatically
|
|
208
|
+
return FunctionReturn(
|
|
209
|
+
returned_values={
|
|
210
|
+
"n": n - 1,
|
|
211
|
+
"a": b,
|
|
212
|
+
"b": a + b
|
|
213
|
+
},
|
|
214
|
+
next_function_to_call="fibonacci"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
engine = IterativeRecursionEngine()
|
|
218
|
+
engine.add_function(fibonacci)
|
|
219
|
+
engine.start_function_caller(
|
|
220
|
+
next_function_to_call="fibonacci",
|
|
221
|
+
environment_variables={"n": 10, "a": 0, "b": 1},
|
|
222
|
+
arg_env_mapping={"n": "n", "a": "a", "b": "b"}
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
print(f"Fibonacci(10) = {engine.environment_variables['result']}") # Output: 55
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Preventing Infinite Loops
|
|
229
|
+
|
|
230
|
+
Use the `max_iterations` parameter to prevent runaway execution:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from iterativerecursion import IterativeRecursionEngine, FunctionReturn
|
|
234
|
+
|
|
235
|
+
def infinite_loop(counter: int) -> FunctionReturn:
|
|
236
|
+
print(f"Iteration: {counter}")
|
|
237
|
+
# Auto-mapping: no need for {"counter": "counter"}
|
|
238
|
+
return FunctionReturn(
|
|
239
|
+
returned_values={"counter": counter + 1},
|
|
240
|
+
next_function_to_call="infinite_loop"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
engine = IterativeRecursionEngine()
|
|
244
|
+
engine.add_function(infinite_loop)
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
engine.start_function_caller(
|
|
248
|
+
next_function_to_call="infinite_loop",
|
|
249
|
+
environment_variables={"counter": 0},
|
|
250
|
+
arg_env_mapping={"counter": "counter"},
|
|
251
|
+
max_iterations=10 # Safety limit
|
|
252
|
+
)
|
|
253
|
+
except RuntimeError as e:
|
|
254
|
+
print(f"Caught: {e}")
|
|
255
|
+
# Output: Caught: Maximum iteration limit (10) reached...
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## API Reference
|
|
259
|
+
|
|
260
|
+
### `IterativeRecursionEngine`
|
|
261
|
+
|
|
262
|
+
The main execution engine for iterative recursion.
|
|
263
|
+
|
|
264
|
+
#### Methods
|
|
265
|
+
|
|
266
|
+
##### `__init__()`
|
|
267
|
+
Creates a new engine instance.
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
engine = IterativeRecursionEngine()
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
##### `add_function(function)`
|
|
274
|
+
Registers a function with the engine.
|
|
275
|
+
|
|
276
|
+
- **Parameters**: `function` - A callable that returns `FunctionReturn`
|
|
277
|
+
- **Returns**: None
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
engine.add_function(my_function)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
##### `register(function)`
|
|
284
|
+
Decorator to register a function with the engine. Alternative to `add_function()`.
|
|
285
|
+
|
|
286
|
+
- **Parameters**: `function` - A callable that returns `FunctionReturn`
|
|
287
|
+
- **Returns**: The same function (for chaining)
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
@engine.register
|
|
291
|
+
def my_function(x: int) -> FunctionReturn:
|
|
292
|
+
return FunctionReturn(
|
|
293
|
+
returned_values={"result": x * 2}
|
|
294
|
+
)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
##### `add_environment_variables(variables: dict[str, Any])`
|
|
298
|
+
Adds or updates variables in the shared environment.
|
|
299
|
+
|
|
300
|
+
- **Parameters**: `variables` - Dictionary of variable names and values
|
|
301
|
+
- **Returns**: None
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
engine.add_environment_variables({"x": 10, "y": 20})
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
##### `start_function_caller(next_function_to_call, environment_variables, arg_env_mapping, max_iterations=None)`
|
|
308
|
+
Begins executing functions starting from the specified function.
|
|
309
|
+
|
|
310
|
+
- **Parameters**:
|
|
311
|
+
- `next_function_to_call` (str): Name of the first function to call
|
|
312
|
+
- `environment_variables` (dict[str, Any]): Initial environment variables
|
|
313
|
+
- `arg_env_mapping` (dict[str, str]): Parameter mapping for first function
|
|
314
|
+
- `max_iterations` (int | None): Maximum iterations allowed (default: None/unlimited)
|
|
315
|
+
- **Returns**: `dict[str, Any]` - Final state of environment variables after execution
|
|
316
|
+
- **Raises**:
|
|
317
|
+
- `KeyError`: If function not found or environment variable missing
|
|
318
|
+
- `RuntimeError`: If `max_iterations` limit is reached
|
|
319
|
+
- `ValueError`: If function returns invalid structure
|
|
320
|
+
- `TypeError`: If function return has wrong types
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
result = engine.start_function_caller(
|
|
324
|
+
next_function_to_call="start_func",
|
|
325
|
+
environment_variables={"value": 42},
|
|
326
|
+
arg_env_mapping={"param": "value"},
|
|
327
|
+
max_iterations=1000
|
|
328
|
+
)
|
|
329
|
+
# Access final state directly from result
|
|
330
|
+
print(result["some_value"])
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### Attributes
|
|
334
|
+
|
|
335
|
+
- `functions_dict` (dict): Registry of available functions
|
|
336
|
+
- `environment_variables` (dict): Shared state accessible to all functions
|
|
337
|
+
|
|
338
|
+
### Type Definitions
|
|
339
|
+
|
|
340
|
+
#### `FunctionReturn`
|
|
341
|
+
Dataclass defining the required return structure for functions.
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
@dataclass
|
|
345
|
+
class FunctionReturn:
|
|
346
|
+
returned_values: dict[str, Any]
|
|
347
|
+
next_function_to_call: str | None = None
|
|
348
|
+
arg_env_mapping: dict[str, str] = field(default_factory=dict)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Auto-mapping feature**: If `arg_env_mapping` is not provided, it automatically maps each key in `returned_values` to itself. This means you rarely need to specify `arg_env_mapping` explicitly.
|
|
352
|
+
|
|
353
|
+
#### `VarsDict`
|
|
354
|
+
Type alias for variable dictionaries.
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
VarsDict = dict[str, Any]
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Development
|
|
361
|
+
|
|
362
|
+
### Running Tests
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# Install with dev dependencies
|
|
366
|
+
pip install -e ".[dev]"
|
|
367
|
+
|
|
368
|
+
# Run tests
|
|
369
|
+
pytest tests/ -v
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Test Coverage
|
|
373
|
+
|
|
374
|
+
The test suite includes:
|
|
375
|
+
- Basic function chaining
|
|
376
|
+
- Environment variable management
|
|
377
|
+
- Error handling and validation
|
|
378
|
+
- Runtime validation of return structures
|
|
379
|
+
- Iteration limits
|
|
380
|
+
- Decorator API (`@register`)
|
|
381
|
+
- Return value access
|
|
382
|
+
- Improved error messages
|
|
383
|
+
- Complex scenarios (factorial, state machines)
|
|
384
|
+
|
|
385
|
+
## Contributing
|
|
386
|
+
|
|
387
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
388
|
+
|
|
389
|
+
## License
|
|
390
|
+
|
|
391
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
392
|
+
|
|
393
|
+
## Author
|
|
394
|
+
|
|
395
|
+
Carlos A. Planchón - [GitHub](https://github.com/carlosplanchon/iterativerecursion)
|
|
396
|
+
|
|
397
|
+
## Acknowledgments
|
|
398
|
+
|
|
399
|
+
This module was created as both a practical solution for deep recursion scenarios and an exploration of alternative execution patterns in Python.
|