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.
Files changed (58) hide show
  1. {invocation_tree-0.0.28/invocation_tree.egg-info → invocation_tree-0.0.30}/PKG-INFO +21 -14
  2. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/README.md +20 -13
  3. invocation_tree-0.0.30/images/__pycache__/graph.cpython-313.pyc +0 -0
  4. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree/__init__.py +12 -12
  5. {invocation_tree-0.0.28 → invocation_tree-0.0.30/invocation_tree.egg-info}/PKG-INFO +21 -14
  6. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/SOURCES.txt +1 -1
  7. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/pyproject.toml +1 -1
  8. invocation_tree-0.0.28/invocation_tree/test_regex_fix.py +0 -31
  9. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/LICENSE.txt +0 -0
  10. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/MANIFEST.in +0 -0
  11. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/compute.gif +0 -0
  12. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/compute.py +0 -0
  13. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/create_gif.sh +0 -0
  14. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/create_images.sh +0 -0
  15. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/draw_graph.py +0 -0
  16. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/draw_graph_d_x.py +0 -0
  17. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_big.out +0 -0
  18. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_big_d_x.out +0 -0
  19. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_small.out +0 -0
  20. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/edges_to_dict.py +0 -0
  21. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/factorial.gif +0 -0
  22. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/factorial.py +0 -0
  23. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph.png.png +0 -0
  24. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph.py +0 -0
  25. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph_big.png +0 -0
  26. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph_big_d_x.png +0 -0
  27. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/graph_small.png +0 -0
  28. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/jugs.png +0 -0
  29. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/jugs_depth_first.py +0 -0
  30. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/perms_LR3.png +0 -0
  31. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations.gif +0 -0
  32. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations.py +0 -0
  33. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_collect.gif +0 -0
  34. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_collect.py +0 -0
  35. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_dot.py +0 -0
  36. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_neighbor.gif +0 -0
  37. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_neighbor.py +0 -0
  38. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_return.gif +0 -0
  39. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_return.py +0 -0
  40. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/permutations_vscode.gif +0 -0
  41. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/print_all_paths.py +0 -0
  42. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/print_all_paths_of_length.py +0 -0
  43. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/print_all_paths_via.py +0 -0
  44. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/puz.txt +0 -0
  45. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/quick_sort.gif +0 -0
  46. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/quick_sort.py +0 -0
  47. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/students.gif +0 -0
  48. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/students.py +0 -0
  49. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/test.pdf +0 -0
  50. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/test.py +0 -0
  51. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/tree.pdf +0 -0
  52. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/tree_problem.gv +0 -0
  53. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/images/vscode.png +0 -0
  54. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree/regex_set.py +0 -0
  55. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/dependency_links.txt +0 -0
  56. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/requires.txt +0 -0
  57. {invocation_tree-0.0.28 → invocation_tree-0.0.30}/invocation_tree.egg-info/top_level.txt +0 -0
  58. {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.28
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
- # Chapters #
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 recursion and iteration. Lets first look at computing the factorial of 4.
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
  ![permutations](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/permutations_neighbor.gif)
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&timestep=0.2&play)
355
355
 
356
- Add temporary debug prints wherever behavior isn’t clear. Experiment with what/how you print to maximize clarity.
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
  <!-- ![permutations_return](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/permutations_return.gif) -->
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&timestep=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 can also be faster as it avoids many list copies as a result of the '+=' list concatenations.
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, so here we do need to use the return to get the result.
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
  <!-- ![quick_sort](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/quick_sort.gif) -->
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&timestep=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 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:
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
- **exercise6:** Write a recursive solver for the Jugs Puzzle that uses less memory by searching for the solution in a depth-first manner.
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
- **solution exercise6:** First try it yourself, but 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.
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
- to hide all function of `namespace`.
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
- # Chapters #
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 recursion and iteration. Lets first look at computing the factorial of 4.
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
  ![permutations](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/permutations_neighbor.gif)
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&timestep=0.2&play)
310
310
 
311
- Add temporary debug prints wherever behavior isn’t clear. Experiment with what/how you print to maximize clarity.
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
  <!-- ![permutations_return](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/permutations_return.gif) -->
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&timestep=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 can also be faster as it avoids many list copies as a result of the '+=' list concatenations.
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, so here we do need to use the return to get the result.
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
  <!-- ![quick_sort](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/quick_sort.gif) -->
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&timestep=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 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:
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
- **exercise6:** Write a recursive solver for the Jugs Puzzle that uses less memory by searching for the solution in a depth-first manner.
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
- **solution exercise6:** First try it yourself, but 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.
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
- to hide all function of `namespace`.
524
+ ignores all function of `namespace`.
518
525
 
519
526
  ## Blocking ##
520
527
 
@@ -9,7 +9,7 @@ import difflib
9
9
 
10
10
  import invocation_tree.regex_set as regset
11
11
 
12
- __version__ = "0.0.28"
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 not self.regset_hide_calls.match(class_fun_name, self.hide_calls):
297
- if event == 'return':
298
- if class_fun_name == self.ignoring_call:
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
- if event == 'call':
304
- if self.regset_ignore_calls.match(class_fun_name, self.ignore_calls):
305
- self.ignoring_call = class_fun_name
306
- return
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.28
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
- # Chapters #
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 recursion and iteration. Lets first look at computing the factorial of 4.
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
  ![permutations](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/permutations_neighbor.gif)
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&timestep=0.2&play)
355
355
 
356
- Add temporary debug prints wherever behavior isn’t clear. Experiment with what/how you print to maximize clarity.
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
  <!-- ![permutations_return](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/permutations_return.gif) -->
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&timestep=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 can also be faster as it avoids many list copies as a result of the '+=' list concatenations.
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, so here we do need to use the return to get the result.
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
  <!-- ![quick_sort](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/quick_sort.gif) -->
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&timestep=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 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:
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
- **exercise6:** Write a recursive solver for the Jugs Puzzle that uses less memory by searching for the solution in a depth-first manner.
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
- **solution exercise6:** First try it yourself, but 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.
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
- to hide all function of `namespace`.
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "invocation_tree"
7
- version = "0.0.28"
7
+ version = "0.0.30"
8
8
  description = "Generates an invocation tree of functions calls."
9
9
  authors = [
10
10
  {name = "Bas Terwijn", email = "bterwijn@gmail.com"}
@@ -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})")