invocation-tree 0.0.28__tar.gz → 0.0.30__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.
- {invocation_tree-0.0.28/invocation_tree.egg-info → invocation_tree-0.0.30}/PKG-INFO +21 -14
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/README.md +20 -13
- invocation_tree-0.0.30/images/__pycache__/graph.cpython-313.pyc +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree/__init__.py +12 -12
- {invocation_tree-0.0.28 → invocation_tree-0.0.30/invocation_tree.egg-info}/PKG-INFO +21 -14
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/SOURCES.txt +1 -1
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/pyproject.toml +1 -1
- invocation_tree-0.0.28/invocation_tree/test_regex_fix.py +0 -31
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/LICENSE.txt +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/MANIFEST.in +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/compute.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/compute.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/create_gif.sh +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/create_images.sh +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/draw_graph.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/draw_graph_d_x.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_big.out +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_big_d_x.out +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_small.out +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_to_dict.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/factorial.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/factorial.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph.png.png +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph_big.png +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph_big_d_x.png +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph_small.png +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/jugs.png +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/jugs_depth_first.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/perms_LR3.png +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_collect.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_collect.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_dot.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_neighbor.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_neighbor.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_return.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_return.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_vscode.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/print_all_paths.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/print_all_paths_of_length.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/print_all_paths_via.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/puz.txt +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/quick_sort.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/quick_sort.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/students.gif +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/students.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/test.pdf +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/test.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/tree.pdf +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/tree_problem.gv +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/vscode.png +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree/regex_set.py +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/dependency_links.txt +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/requires.txt +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/top_level.txt +0 -0
- {invocation_tree-0.0.28 → invocation_tree-0.0.30}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invocation_tree
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.30
|
|
4
4
|
Summary: Generates an invocation tree of functions calls.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License: BSD 2-Clause License
|
|
@@ -57,7 +57,7 @@ Run a live demo in the 👉 [**Invocation Tree Web Debugger**](https://invocatio
|
|
|
57
57
|
- shows the invocation tree (call tree) of a program **in real time**
|
|
58
58
|
- helps to **understand recursion** and its depth-first nature
|
|
59
59
|
|
|
60
|
-
#
|
|
60
|
+
# Topics #
|
|
61
61
|
|
|
62
62
|
[Iteration and Recursion](#iteration-and-recursion)
|
|
63
63
|
|
|
@@ -92,7 +92,7 @@ ___
|
|
|
92
92
|
|
|
93
93
|
# Iteration and Recursion #
|
|
94
94
|
|
|
95
|
-
Repetion can be implemented with
|
|
95
|
+
Repetion can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
|
|
96
96
|
|
|
97
97
|
``` python
|
|
98
98
|
import math
|
|
@@ -102,7 +102,7 @@ print(math.factorial(4))
|
|
|
102
102
|
```
|
|
103
103
|
24
|
|
104
104
|
```
|
|
105
|
-
The result is `1 * 2 * 3 * 4 = 24`.
|
|
105
|
+
The result is computed with repeated multiplication: `1 * 2 * 3 * 4 = 24`.
|
|
106
106
|
|
|
107
107
|
To implement our own factorial function we can use iteration, a for-loop or while-loop, like so:
|
|
108
108
|
|
|
@@ -119,7 +119,7 @@ print(factorial(4))
|
|
|
119
119
|
24
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
Or we can use recursion, a function that calls itself. Then we also need a stop condition to prevent the function from calling itself indefinitely, like so:
|
|
122
|
+
Or we can use recursion, a function that calls itself. Then we also need a **base case** as **stop condition** to prevent the function from calling itself indefinitely, like so:
|
|
123
123
|
|
|
124
124
|
```python
|
|
125
125
|
def factorial(n):
|
|
@@ -287,7 +287,7 @@ CBC
|
|
|
287
287
|

|
|
288
288
|
Or see it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/permutations_neighbor.py)
|
|
289
289
|
|
|
290
|
-
With recursion we can stop neighbors from being equal early, in contrast to iteration, where we would have had to filter out a permutation with equal neighbors after it was fully generated, which could be much slower and would require more complex program.
|
|
290
|
+
With recursion we can stop neighbors from being equal early, in contrast to iteration, where we would have had to filter out a permutation with equal neighbors after it was fully generated, which could be much slower and would require a more complex program.
|
|
291
291
|
|
|
292
292
|
**exercise3:** Print all permutations with replacements of elements 'A', 'B', and 'C' of length 5 that are palindrome ('ABABA' is palindrome because if you read it backwards it's the same).
|
|
293
293
|
|
|
@@ -353,7 +353,7 @@ Adding temporarely debug print statements is in general a good technique to unde
|
|
|
353
353
|
|
|
354
354
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/print_paths_print_1.py&breakpoints=33&continues=1×tep=0.2&play)
|
|
355
355
|
|
|
356
|
-
Add temporary debug prints wherever behavior isn’t clear. Experiment with what
|
|
356
|
+
Add temporary debug prints wherever behavior isn’t clear. Experiment with what and how you print to maximize clarity.
|
|
357
357
|
|
|
358
358
|
**exercise4:** In this larger bidirectional graph, print all the paths of length 7 that connect node `a` to node `b` where going over the same node multiple times is allowed (`avjxbxb` is one such path, there are 114 such paths in total).
|
|
359
359
|
|
|
@@ -392,7 +392,7 @@ print(tree(permutations, 'LR', '', 3))
|
|
|
392
392
|
<!--  -->
|
|
393
393
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/permutations_return.py×tep=0.5&play)
|
|
394
394
|
|
|
395
|
-
However, sometimes it is easier to pass a list (or other mutable container type) as an extra argument in the recursion to collect the results in. This
|
|
395
|
+
However, sometimes it is easier to pass a list (or any other mutable container type) as an extra argument in the recursion to collect the results in. This is also faster because now just one list is used instead of each function call having to create its own `results` list.
|
|
396
396
|
|
|
397
397
|
```python
|
|
398
398
|
import invocation_tree as ivt
|
|
@@ -432,7 +432,7 @@ edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e')
|
|
|
432
432
|
|
|
433
433
|
# Quick Sort #
|
|
434
434
|
|
|
435
|
-
Another nice example of divide-and-conquer is the recursive quicksort algorithm. It works by choosing a pivot element and dividing the list into elements smaller than the pivot and elements larger than the pivot. Each of these sublists is then quicksorted in the same way. When we get to the point a sublist has zero or one element, it is already sorted. When returning, these sorted sublists are then combined with the pivot to produce a larger sorted lists
|
|
435
|
+
Another nice example of divide-and-conquer is the recursive quicksort algorithm. It works by choosing a pivot element and dividing the list into elements smaller than the pivot and elements larger than the pivot. Each of these sublists is then quicksorted in the same way. When we get to the point a sublist has zero or one element, it is already sorted. When returning, these sorted sublists are then combined with the pivot to produce a larger sorted lists. Here we use the return value to get the sorted result.
|
|
436
436
|
|
|
437
437
|
```python
|
|
438
438
|
import invocation_tree as ivt
|
|
@@ -459,9 +459,11 @@ unsorted values: [7, 4, 10, 11, 2, 6, 9, 1, 5, 3, 8, 12]
|
|
|
459
459
|
<!--  -->
|
|
460
460
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/quick_sort.py×tep=0.5&play)
|
|
461
461
|
|
|
462
|
+
**exercise6:** Rewrite this quick sort program so that we can pass in a list to collect the sorted result and we don't need to use a return value. This would make the program faster as it avoids having to use the `+` list concatenation operator that creates a new list each time we use it, whereas `+=` or `append()` only add to an existing list.
|
|
463
|
+
|
|
462
464
|
# Jugs Puzzle #
|
|
463
465
|
|
|
464
|
-
In the Jugs
|
|
466
|
+
In the Jugs Puzzle we have a set of jugs of various capacities. Our goal is to get a jug with a certain amount of liquid in it. In each step we can take one of these actions:
|
|
465
467
|
|
|
466
468
|
- fill one jug until it is full
|
|
467
469
|
- empty one jug until it is empty
|
|
@@ -515,12 +517,14 @@ Where:
|
|
|
515
517
|
|
|
516
518
|
The breadth-first algorithm works and gives us the shortest path to a goal state, but to do that it uses a lot of memory to store each generation and all jugs states it has seen. Now we also want an algorithm that uses much less memory.
|
|
517
519
|
|
|
518
|
-
**
|
|
520
|
+
**exercise7:** Write a recursive solver for the Jugs Puzzle that uses less memory by searching for the solution in a depth-first manner.
|
|
519
521
|
|
|
520
522
|
- A solution may not have the same jugs state multiple times (this also avoids infinite loops).
|
|
521
523
|
- It is not necessary to find the shortest path to a goal state (like breadth-first does).
|
|
522
524
|
|
|
523
|
-
|
|
525
|
+
If you want to use the Invocation Tree Web Debugger, you can look at these [configuration examples](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py) to keep the tree small and readable.
|
|
526
|
+
|
|
527
|
+
**solution exercise7:** First try it yourself, we give the [solution](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/jugs_depth_first.py&breakpoints=136&continues=1) here for comparison.
|
|
524
528
|
|
|
525
529
|
A harder more fun instance of this puzzle is with jugs with capacity 3, 5, 34 and 107 liter and the goal of getting to a jug with 51 liters.
|
|
526
530
|
|
|
@@ -529,12 +533,14 @@ $ python jugs_breadth_first.py 51 3,5,34,107
|
|
|
529
533
|
```
|
|
530
534
|
|
|
531
535
|
# Configuration #
|
|
536
|
+
The Invocation Tree Web Debugger gives examples of the [most important configurations](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py).
|
|
532
537
|
|
|
533
538
|
## Hidding ##
|
|
534
539
|
It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
|
|
535
540
|
|
|
536
541
|
```python
|
|
537
542
|
tree = ivt.blocking()
|
|
543
|
+
tree.hide_vars.add('functionname.variablename') # or:
|
|
538
544
|
tree.hide_vars.add('namespace.functionname.variablename')
|
|
539
545
|
```
|
|
540
546
|
|
|
@@ -542,6 +548,7 @@ Or hide certain function calls:
|
|
|
542
548
|
|
|
543
549
|
```python
|
|
544
550
|
tree = ivt.blocking()
|
|
551
|
+
tree.hide_calls.add('functionname') # or:
|
|
545
552
|
tree.hide_calls.add('namespace.functionname')
|
|
546
553
|
```
|
|
547
554
|
|
|
@@ -556,10 +563,10 @@ With the `re:` prefix we can use regular expresssions, for example:
|
|
|
556
563
|
|
|
557
564
|
```python
|
|
558
565
|
tree = ivt.blocking()
|
|
559
|
-
tree.ignore_calls.add('re:namespace\..*')
|
|
566
|
+
tree.ignore_calls.add(r're:namespace\..*')
|
|
560
567
|
```
|
|
561
568
|
|
|
562
|
-
|
|
569
|
+
ignores all function of `namespace`.
|
|
563
570
|
|
|
564
571
|
## Blocking ##
|
|
565
572
|
|
|
@@ -12,7 +12,7 @@ Run a live demo in the 👉 [**Invocation Tree Web Debugger**](https://invocatio
|
|
|
12
12
|
- shows the invocation tree (call tree) of a program **in real time**
|
|
13
13
|
- helps to **understand recursion** and its depth-first nature
|
|
14
14
|
|
|
15
|
-
#
|
|
15
|
+
# Topics #
|
|
16
16
|
|
|
17
17
|
[Iteration and Recursion](#iteration-and-recursion)
|
|
18
18
|
|
|
@@ -47,7 +47,7 @@ ___
|
|
|
47
47
|
|
|
48
48
|
# Iteration and Recursion #
|
|
49
49
|
|
|
50
|
-
Repetion can be implemented with
|
|
50
|
+
Repetion can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
|
|
51
51
|
|
|
52
52
|
``` python
|
|
53
53
|
import math
|
|
@@ -57,7 +57,7 @@ print(math.factorial(4))
|
|
|
57
57
|
```
|
|
58
58
|
24
|
|
59
59
|
```
|
|
60
|
-
The result is `1 * 2 * 3 * 4 = 24`.
|
|
60
|
+
The result is computed with repeated multiplication: `1 * 2 * 3 * 4 = 24`.
|
|
61
61
|
|
|
62
62
|
To implement our own factorial function we can use iteration, a for-loop or while-loop, like so:
|
|
63
63
|
|
|
@@ -74,7 +74,7 @@ print(factorial(4))
|
|
|
74
74
|
24
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
Or we can use recursion, a function that calls itself. Then we also need a stop condition to prevent the function from calling itself indefinitely, like so:
|
|
77
|
+
Or we can use recursion, a function that calls itself. Then we also need a **base case** as **stop condition** to prevent the function from calling itself indefinitely, like so:
|
|
78
78
|
|
|
79
79
|
```python
|
|
80
80
|
def factorial(n):
|
|
@@ -242,7 +242,7 @@ CBC
|
|
|
242
242
|

|
|
243
243
|
Or see it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/permutations_neighbor.py)
|
|
244
244
|
|
|
245
|
-
With recursion we can stop neighbors from being equal early, in contrast to iteration, where we would have had to filter out a permutation with equal neighbors after it was fully generated, which could be much slower and would require more complex program.
|
|
245
|
+
With recursion we can stop neighbors from being equal early, in contrast to iteration, where we would have had to filter out a permutation with equal neighbors after it was fully generated, which could be much slower and would require a more complex program.
|
|
246
246
|
|
|
247
247
|
**exercise3:** Print all permutations with replacements of elements 'A', 'B', and 'C' of length 5 that are palindrome ('ABABA' is palindrome because if you read it backwards it's the same).
|
|
248
248
|
|
|
@@ -308,7 +308,7 @@ Adding temporarely debug print statements is in general a good technique to unde
|
|
|
308
308
|
|
|
309
309
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/print_paths_print_1.py&breakpoints=33&continues=1×tep=0.2&play)
|
|
310
310
|
|
|
311
|
-
Add temporary debug prints wherever behavior isn’t clear. Experiment with what
|
|
311
|
+
Add temporary debug prints wherever behavior isn’t clear. Experiment with what and how you print to maximize clarity.
|
|
312
312
|
|
|
313
313
|
**exercise4:** In this larger bidirectional graph, print all the paths of length 7 that connect node `a` to node `b` where going over the same node multiple times is allowed (`avjxbxb` is one such path, there are 114 such paths in total).
|
|
314
314
|
|
|
@@ -347,7 +347,7 @@ print(tree(permutations, 'LR', '', 3))
|
|
|
347
347
|
<!--  -->
|
|
348
348
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/permutations_return.py×tep=0.5&play)
|
|
349
349
|
|
|
350
|
-
However, sometimes it is easier to pass a list (or other mutable container type) as an extra argument in the recursion to collect the results in. This
|
|
350
|
+
However, sometimes it is easier to pass a list (or any other mutable container type) as an extra argument in the recursion to collect the results in. This is also faster because now just one list is used instead of each function call having to create its own `results` list.
|
|
351
351
|
|
|
352
352
|
```python
|
|
353
353
|
import invocation_tree as ivt
|
|
@@ -387,7 +387,7 @@ edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e')
|
|
|
387
387
|
|
|
388
388
|
# Quick Sort #
|
|
389
389
|
|
|
390
|
-
Another nice example of divide-and-conquer is the recursive quicksort algorithm. It works by choosing a pivot element and dividing the list into elements smaller than the pivot and elements larger than the pivot. Each of these sublists is then quicksorted in the same way. When we get to the point a sublist has zero or one element, it is already sorted. When returning, these sorted sublists are then combined with the pivot to produce a larger sorted lists
|
|
390
|
+
Another nice example of divide-and-conquer is the recursive quicksort algorithm. It works by choosing a pivot element and dividing the list into elements smaller than the pivot and elements larger than the pivot. Each of these sublists is then quicksorted in the same way. When we get to the point a sublist has zero or one element, it is already sorted. When returning, these sorted sublists are then combined with the pivot to produce a larger sorted lists. Here we use the return value to get the sorted result.
|
|
391
391
|
|
|
392
392
|
```python
|
|
393
393
|
import invocation_tree as ivt
|
|
@@ -414,9 +414,11 @@ unsorted values: [7, 4, 10, 11, 2, 6, 9, 1, 5, 3, 8, 12]
|
|
|
414
414
|
<!--  -->
|
|
415
415
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/quick_sort.py×tep=0.5&play)
|
|
416
416
|
|
|
417
|
+
**exercise6:** Rewrite this quick sort program so that we can pass in a list to collect the sorted result and we don't need to use a return value. This would make the program faster as it avoids having to use the `+` list concatenation operator that creates a new list each time we use it, whereas `+=` or `append()` only add to an existing list.
|
|
418
|
+
|
|
417
419
|
# Jugs Puzzle #
|
|
418
420
|
|
|
419
|
-
In the Jugs
|
|
421
|
+
In the Jugs Puzzle we have a set of jugs of various capacities. Our goal is to get a jug with a certain amount of liquid in it. In each step we can take one of these actions:
|
|
420
422
|
|
|
421
423
|
- fill one jug until it is full
|
|
422
424
|
- empty one jug until it is empty
|
|
@@ -470,12 +472,14 @@ Where:
|
|
|
470
472
|
|
|
471
473
|
The breadth-first algorithm works and gives us the shortest path to a goal state, but to do that it uses a lot of memory to store each generation and all jugs states it has seen. Now we also want an algorithm that uses much less memory.
|
|
472
474
|
|
|
473
|
-
**
|
|
475
|
+
**exercise7:** Write a recursive solver for the Jugs Puzzle that uses less memory by searching for the solution in a depth-first manner.
|
|
474
476
|
|
|
475
477
|
- A solution may not have the same jugs state multiple times (this also avoids infinite loops).
|
|
476
478
|
- It is not necessary to find the shortest path to a goal state (like breadth-first does).
|
|
477
479
|
|
|
478
|
-
|
|
480
|
+
If you want to use the Invocation Tree Web Debugger, you can look at these [configuration examples](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py) to keep the tree small and readable.
|
|
481
|
+
|
|
482
|
+
**solution exercise7:** First try it yourself, we give the [solution](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/jugs_depth_first.py&breakpoints=136&continues=1) here for comparison.
|
|
479
483
|
|
|
480
484
|
A harder more fun instance of this puzzle is with jugs with capacity 3, 5, 34 and 107 liter and the goal of getting to a jug with 51 liters.
|
|
481
485
|
|
|
@@ -484,12 +488,14 @@ $ python jugs_breadth_first.py 51 3,5,34,107
|
|
|
484
488
|
```
|
|
485
489
|
|
|
486
490
|
# Configuration #
|
|
491
|
+
The Invocation Tree Web Debugger gives examples of the [most important configurations](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py).
|
|
487
492
|
|
|
488
493
|
## Hidding ##
|
|
489
494
|
It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
|
|
490
495
|
|
|
491
496
|
```python
|
|
492
497
|
tree = ivt.blocking()
|
|
498
|
+
tree.hide_vars.add('functionname.variablename') # or:
|
|
493
499
|
tree.hide_vars.add('namespace.functionname.variablename')
|
|
494
500
|
```
|
|
495
501
|
|
|
@@ -497,6 +503,7 @@ Or hide certain function calls:
|
|
|
497
503
|
|
|
498
504
|
```python
|
|
499
505
|
tree = ivt.blocking()
|
|
506
|
+
tree.hide_calls.add('functionname') # or:
|
|
500
507
|
tree.hide_calls.add('namespace.functionname')
|
|
501
508
|
```
|
|
502
509
|
|
|
@@ -511,10 +518,10 @@ With the `re:` prefix we can use regular expresssions, for example:
|
|
|
511
518
|
|
|
512
519
|
```python
|
|
513
520
|
tree = ivt.blocking()
|
|
514
|
-
tree.ignore_calls.add('re:namespace\..*')
|
|
521
|
+
tree.ignore_calls.add(r're:namespace\..*')
|
|
515
522
|
```
|
|
516
523
|
|
|
517
|
-
|
|
524
|
+
ignores all function of `namespace`.
|
|
518
525
|
|
|
519
526
|
## Blocking ##
|
|
520
527
|
|
|
Binary file
|
|
@@ -9,7 +9,7 @@ import difflib
|
|
|
9
9
|
|
|
10
10
|
import invocation_tree.regex_set as regset
|
|
11
11
|
|
|
12
|
-
__version__ = "0.0.
|
|
12
|
+
__version__ = "0.0.30"
|
|
13
13
|
__author__ = 'Bas Terwijn'
|
|
14
14
|
|
|
15
15
|
def highlight_diff(str1, str2):
|
|
@@ -112,7 +112,7 @@ class Invocation_Tree:
|
|
|
112
112
|
self.is_highlighted = False
|
|
113
113
|
self.graph = None
|
|
114
114
|
self.prev_global_tracer = None
|
|
115
|
-
self.hide_calls = {'Invocation_Tree.__exit__', 'Invocation_Tree.stop_trace'}
|
|
115
|
+
self.hide_calls = {'Invocation_Tree.__exit__', 'Invocation_Tree.stop_trace', '<genexpr>'}
|
|
116
116
|
self.ignore_calls = set()
|
|
117
117
|
self.ignoring_call = None
|
|
118
118
|
self.regset_hide_vars = regset.Regex_Set(self.hide_vars)
|
|
@@ -293,17 +293,17 @@ class Invocation_Tree:
|
|
|
293
293
|
if self.is_external(frame):
|
|
294
294
|
return
|
|
295
295
|
class_fun_name = get_class_function_name(frame)
|
|
296
|
-
if
|
|
297
|
-
if
|
|
298
|
-
|
|
299
|
-
self.ignoring_call = None
|
|
300
|
-
return
|
|
301
|
-
if self.ignoring_call is not None:
|
|
296
|
+
if event == 'return':
|
|
297
|
+
if class_fun_name == self.ignoring_call:
|
|
298
|
+
self.ignoring_call = None
|
|
302
299
|
return
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
300
|
+
if self.ignoring_call is not None:
|
|
301
|
+
return
|
|
302
|
+
if event == 'call':
|
|
303
|
+
if self.regset_ignore_calls.match(class_fun_name, self.ignore_calls):
|
|
304
|
+
self.ignoring_call = class_fun_name
|
|
305
|
+
return
|
|
306
|
+
if not self.regset_hide_calls.match(class_fun_name, self.hide_calls):
|
|
307
307
|
if event == 'call':
|
|
308
308
|
# update previous active node with its current frame now
|
|
309
309
|
if len(self.stack)>0:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invocation_tree
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.30
|
|
4
4
|
Summary: Generates an invocation tree of functions calls.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License: BSD 2-Clause License
|
|
@@ -57,7 +57,7 @@ Run a live demo in the 👉 [**Invocation Tree Web Debugger**](https://invocatio
|
|
|
57
57
|
- shows the invocation tree (call tree) of a program **in real time**
|
|
58
58
|
- helps to **understand recursion** and its depth-first nature
|
|
59
59
|
|
|
60
|
-
#
|
|
60
|
+
# Topics #
|
|
61
61
|
|
|
62
62
|
[Iteration and Recursion](#iteration-and-recursion)
|
|
63
63
|
|
|
@@ -92,7 +92,7 @@ ___
|
|
|
92
92
|
|
|
93
93
|
# Iteration and Recursion #
|
|
94
94
|
|
|
95
|
-
Repetion can be implemented with
|
|
95
|
+
Repetion can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
|
|
96
96
|
|
|
97
97
|
``` python
|
|
98
98
|
import math
|
|
@@ -102,7 +102,7 @@ print(math.factorial(4))
|
|
|
102
102
|
```
|
|
103
103
|
24
|
|
104
104
|
```
|
|
105
|
-
The result is `1 * 2 * 3 * 4 = 24`.
|
|
105
|
+
The result is computed with repeated multiplication: `1 * 2 * 3 * 4 = 24`.
|
|
106
106
|
|
|
107
107
|
To implement our own factorial function we can use iteration, a for-loop or while-loop, like so:
|
|
108
108
|
|
|
@@ -119,7 +119,7 @@ print(factorial(4))
|
|
|
119
119
|
24
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
Or we can use recursion, a function that calls itself. Then we also need a stop condition to prevent the function from calling itself indefinitely, like so:
|
|
122
|
+
Or we can use recursion, a function that calls itself. Then we also need a **base case** as **stop condition** to prevent the function from calling itself indefinitely, like so:
|
|
123
123
|
|
|
124
124
|
```python
|
|
125
125
|
def factorial(n):
|
|
@@ -287,7 +287,7 @@ CBC
|
|
|
287
287
|

|
|
288
288
|
Or see it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/permutations_neighbor.py)
|
|
289
289
|
|
|
290
|
-
With recursion we can stop neighbors from being equal early, in contrast to iteration, where we would have had to filter out a permutation with equal neighbors after it was fully generated, which could be much slower and would require more complex program.
|
|
290
|
+
With recursion we can stop neighbors from being equal early, in contrast to iteration, where we would have had to filter out a permutation with equal neighbors after it was fully generated, which could be much slower and would require a more complex program.
|
|
291
291
|
|
|
292
292
|
**exercise3:** Print all permutations with replacements of elements 'A', 'B', and 'C' of length 5 that are palindrome ('ABABA' is palindrome because if you read it backwards it's the same).
|
|
293
293
|
|
|
@@ -353,7 +353,7 @@ Adding temporarely debug print statements is in general a good technique to unde
|
|
|
353
353
|
|
|
354
354
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/print_paths_print_1.py&breakpoints=33&continues=1×tep=0.2&play)
|
|
355
355
|
|
|
356
|
-
Add temporary debug prints wherever behavior isn’t clear. Experiment with what
|
|
356
|
+
Add temporary debug prints wherever behavior isn’t clear. Experiment with what and how you print to maximize clarity.
|
|
357
357
|
|
|
358
358
|
**exercise4:** In this larger bidirectional graph, print all the paths of length 7 that connect node `a` to node `b` where going over the same node multiple times is allowed (`avjxbxb` is one such path, there are 114 such paths in total).
|
|
359
359
|
|
|
@@ -392,7 +392,7 @@ print(tree(permutations, 'LR', '', 3))
|
|
|
392
392
|
<!--  -->
|
|
393
393
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/permutations_return.py×tep=0.5&play)
|
|
394
394
|
|
|
395
|
-
However, sometimes it is easier to pass a list (or other mutable container type) as an extra argument in the recursion to collect the results in. This
|
|
395
|
+
However, sometimes it is easier to pass a list (or any other mutable container type) as an extra argument in the recursion to collect the results in. This is also faster because now just one list is used instead of each function call having to create its own `results` list.
|
|
396
396
|
|
|
397
397
|
```python
|
|
398
398
|
import invocation_tree as ivt
|
|
@@ -432,7 +432,7 @@ edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e')
|
|
|
432
432
|
|
|
433
433
|
# Quick Sort #
|
|
434
434
|
|
|
435
|
-
Another nice example of divide-and-conquer is the recursive quicksort algorithm. It works by choosing a pivot element and dividing the list into elements smaller than the pivot and elements larger than the pivot. Each of these sublists is then quicksorted in the same way. When we get to the point a sublist has zero or one element, it is already sorted. When returning, these sorted sublists are then combined with the pivot to produce a larger sorted lists
|
|
435
|
+
Another nice example of divide-and-conquer is the recursive quicksort algorithm. It works by choosing a pivot element and dividing the list into elements smaller than the pivot and elements larger than the pivot. Each of these sublists is then quicksorted in the same way. When we get to the point a sublist has zero or one element, it is already sorted. When returning, these sorted sublists are then combined with the pivot to produce a larger sorted lists. Here we use the return value to get the sorted result.
|
|
436
436
|
|
|
437
437
|
```python
|
|
438
438
|
import invocation_tree as ivt
|
|
@@ -459,9 +459,11 @@ unsorted values: [7, 4, 10, 11, 2, 6, 9, 1, 5, 3, 8, 12]
|
|
|
459
459
|
<!--  -->
|
|
460
460
|
See it in the [Invocation Tree Web Debugger](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/quick_sort.py×tep=0.5&play)
|
|
461
461
|
|
|
462
|
+
**exercise6:** Rewrite this quick sort program so that we can pass in a list to collect the sorted result and we don't need to use a return value. This would make the program faster as it avoids having to use the `+` list concatenation operator that creates a new list each time we use it, whereas `+=` or `append()` only add to an existing list.
|
|
463
|
+
|
|
462
464
|
# Jugs Puzzle #
|
|
463
465
|
|
|
464
|
-
In the Jugs
|
|
466
|
+
In the Jugs Puzzle we have a set of jugs of various capacities. Our goal is to get a jug with a certain amount of liquid in it. In each step we can take one of these actions:
|
|
465
467
|
|
|
466
468
|
- fill one jug until it is full
|
|
467
469
|
- empty one jug until it is empty
|
|
@@ -515,12 +517,14 @@ Where:
|
|
|
515
517
|
|
|
516
518
|
The breadth-first algorithm works and gives us the shortest path to a goal state, but to do that it uses a lot of memory to store each generation and all jugs states it has seen. Now we also want an algorithm that uses much less memory.
|
|
517
519
|
|
|
518
|
-
**
|
|
520
|
+
**exercise7:** Write a recursive solver for the Jugs Puzzle that uses less memory by searching for the solution in a depth-first manner.
|
|
519
521
|
|
|
520
522
|
- A solution may not have the same jugs state multiple times (this also avoids infinite loops).
|
|
521
523
|
- It is not necessary to find the shortest path to a goal state (like breadth-first does).
|
|
522
524
|
|
|
523
|
-
|
|
525
|
+
If you want to use the Invocation Tree Web Debugger, you can look at these [configuration examples](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py) to keep the tree small and readable.
|
|
526
|
+
|
|
527
|
+
**solution exercise7:** First try it yourself, we give the [solution](https://www.invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/jugs_depth_first.py&breakpoints=136&continues=1) here for comparison.
|
|
524
528
|
|
|
525
529
|
A harder more fun instance of this puzzle is with jugs with capacity 3, 5, 34 and 107 liter and the goal of getting to a jug with 51 liters.
|
|
526
530
|
|
|
@@ -529,12 +533,14 @@ $ python jugs_breadth_first.py 51 3,5,34,107
|
|
|
529
533
|
```
|
|
530
534
|
|
|
531
535
|
# Configuration #
|
|
536
|
+
The Invocation Tree Web Debugger gives examples of the [most important configurations](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py).
|
|
532
537
|
|
|
533
538
|
## Hidding ##
|
|
534
539
|
It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
|
|
535
540
|
|
|
536
541
|
```python
|
|
537
542
|
tree = ivt.blocking()
|
|
543
|
+
tree.hide_vars.add('functionname.variablename') # or:
|
|
538
544
|
tree.hide_vars.add('namespace.functionname.variablename')
|
|
539
545
|
```
|
|
540
546
|
|
|
@@ -542,6 +548,7 @@ Or hide certain function calls:
|
|
|
542
548
|
|
|
543
549
|
```python
|
|
544
550
|
tree = ivt.blocking()
|
|
551
|
+
tree.hide_calls.add('functionname') # or:
|
|
545
552
|
tree.hide_calls.add('namespace.functionname')
|
|
546
553
|
```
|
|
547
554
|
|
|
@@ -556,10 +563,10 @@ With the `re:` prefix we can use regular expresssions, for example:
|
|
|
556
563
|
|
|
557
564
|
```python
|
|
558
565
|
tree = ivt.blocking()
|
|
559
|
-
tree.ignore_calls.add('re:namespace\..*')
|
|
566
|
+
tree.ignore_calls.add(r're:namespace\..*')
|
|
560
567
|
```
|
|
561
568
|
|
|
562
|
-
|
|
569
|
+
ignores all function of `namespace`.
|
|
563
570
|
|
|
564
571
|
## Blocking ##
|
|
565
572
|
|
|
@@ -45,9 +45,9 @@ images/test.py
|
|
|
45
45
|
images/tree.pdf
|
|
46
46
|
images/tree_problem.gv
|
|
47
47
|
images/vscode.png
|
|
48
|
+
images/__pycache__/graph.cpython-313.pyc
|
|
48
49
|
invocation_tree/__init__.py
|
|
49
50
|
invocation_tree/regex_set.py
|
|
50
|
-
invocation_tree/test_regex_fix.py
|
|
51
51
|
invocation_tree.egg-info/PKG-INFO
|
|
52
52
|
invocation_tree.egg-info/SOURCES.txt
|
|
53
53
|
invocation_tree.egg-info/dependency_links.txt
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
sys.path.append('/home/bterwijn/projects/invocation_tree/invocation_tree')
|
|
3
|
-
from regex_set import Regex_Set
|
|
4
|
-
|
|
5
|
-
# Test the fix
|
|
6
|
-
class_fun_name = 'Jugs.copy'
|
|
7
|
-
ignore_calls = {'J.*'}
|
|
8
|
-
|
|
9
|
-
regset_ignore_calls = Regex_Set(ignore_calls)
|
|
10
|
-
result = regset_ignore_calls.match(class_fun_name, ignore_calls)
|
|
11
|
-
|
|
12
|
-
print(f"class_fun_name: '{class_fun_name}'")
|
|
13
|
-
print(f"ignore_calls: {ignore_calls}")
|
|
14
|
-
print(f"match result: {result}")
|
|
15
|
-
|
|
16
|
-
# Let's also test the compiled pattern
|
|
17
|
-
print(f"compiled pattern: {regset_ignore_calls.compiled_pattern.pattern}")
|
|
18
|
-
|
|
19
|
-
# Test a few more cases
|
|
20
|
-
test_cases = [
|
|
21
|
-
('Jugs.copy', True),
|
|
22
|
-
('J.test', True),
|
|
23
|
-
('Something.else', False),
|
|
24
|
-
('AnotherClass.method', False)
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
print("\nTest cases:")
|
|
28
|
-
for test_string, expected in test_cases:
|
|
29
|
-
result = regset_ignore_calls.match(test_string, ignore_calls)
|
|
30
|
-
status = "✓" if result == expected else "✗"
|
|
31
|
-
print(f" {status} '{test_string}' -> {result} (expected {expected})")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|