invocation-tree 0.0.29__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 (73) hide show
  1. {invocation_tree-0.0.29/invocation_tree.egg-info → invocation_tree-0.0.30}/PKG-INFO +19 -15
  2. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/README.md +18 -14
  3. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/invocation_tree/__init__.py +2 -2
  4. {invocation_tree-0.0.29 → invocation_tree-0.0.30/invocation_tree.egg-info}/PKG-INFO +19 -15
  5. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/invocation_tree.egg-info/SOURCES.txt +0 -16
  6. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/pyproject.toml +1 -1
  7. invocation_tree-0.0.29/images/bdsmlr-123210-XatifIR7GC.jpg +0 -0
  8. invocation_tree-0.0.29/images/bdsmlr-137085-6shuc85rzp.jpg +0 -0
  9. invocation_tree-0.0.29/images/bdsmlr-137085-BgIaVabSuO.jpg +0 -0
  10. invocation_tree-0.0.29/images/bdsmlr-137085-EbOCN2t7pK.jpg +0 -0
  11. invocation_tree-0.0.29/images/bdsmlr-137085-G7VfslodUg.jpg +0 -0
  12. invocation_tree-0.0.29/images/bdsmlr-137085-bECkK7Tmrl.jpg +0 -0
  13. invocation_tree-0.0.29/images/bdsmlr-137085-dwKgKQW44r.jpg +0 -0
  14. invocation_tree-0.0.29/images/bdsmlr-137085-k3Cc6Y1Czs.jpg +0 -0
  15. invocation_tree-0.0.29/images/bdsmlr-137085-lG052p8RGk.jpg +0 -0
  16. invocation_tree-0.0.29/images/bdsmlr-13874-OSFOTjkp0j.jpg +0 -0
  17. invocation_tree-0.0.29/images/bdsmlr-193150-3cpmNpHLOn.jpg +0 -0
  18. invocation_tree-0.0.29/images/bdsmlr-193150-DTYC3stBX0.jpg +0 -0
  19. invocation_tree-0.0.29/images/bdsmlr-193150-FqlVlDXAoY.jpg +0 -0
  20. invocation_tree-0.0.29/images/bdsmlr-193150-hdbOxh4o7w.jpg +0 -0
  21. invocation_tree-0.0.29/images/bdsmlr-193150-i64En9zgET.jpg +0 -0
  22. invocation_tree-0.0.29/invocation_tree/test_regex_fix.py +0 -31
  23. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/LICENSE.txt +0 -0
  24. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/MANIFEST.in +0 -0
  25. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/__pycache__/graph.cpython-313.pyc +0 -0
  26. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/compute.gif +0 -0
  27. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/compute.py +0 -0
  28. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/create_gif.sh +0 -0
  29. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/create_images.sh +0 -0
  30. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/draw_graph.py +0 -0
  31. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/draw_graph_d_x.py +0 -0
  32. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/edges_big.out +0 -0
  33. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/edges_big_d_x.out +0 -0
  34. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/edges_small.out +0 -0
  35. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/edges_to_dict.py +0 -0
  36. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/factorial.gif +0 -0
  37. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/factorial.py +0 -0
  38. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/graph.png.png +0 -0
  39. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/graph.py +0 -0
  40. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/graph_big.png +0 -0
  41. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/graph_big_d_x.png +0 -0
  42. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/graph_small.png +0 -0
  43. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/jugs.png +0 -0
  44. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/jugs_depth_first.py +0 -0
  45. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/perms_LR3.png +0 -0
  46. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations.gif +0 -0
  47. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations.py +0 -0
  48. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_collect.gif +0 -0
  49. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_collect.py +0 -0
  50. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_dot.py +0 -0
  51. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_neighbor.gif +0 -0
  52. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_neighbor.py +0 -0
  53. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_return.gif +0 -0
  54. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_return.py +0 -0
  55. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/permutations_vscode.gif +0 -0
  56. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/print_all_paths.py +0 -0
  57. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/print_all_paths_of_length.py +0 -0
  58. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/print_all_paths_via.py +0 -0
  59. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/puz.txt +0 -0
  60. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/quick_sort.gif +0 -0
  61. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/quick_sort.py +0 -0
  62. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/students.gif +0 -0
  63. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/students.py +0 -0
  64. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/test.pdf +0 -0
  65. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/test.py +0 -0
  66. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/tree.pdf +0 -0
  67. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/tree_problem.gv +0 -0
  68. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/images/vscode.png +0 -0
  69. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/invocation_tree/regex_set.py +0 -0
  70. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/invocation_tree.egg-info/dependency_links.txt +0 -0
  71. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/invocation_tree.egg-info/requires.txt +0 -0
  72. {invocation_tree-0.0.29 → invocation_tree-0.0.30}/invocation_tree.egg-info/top_level.txt +0 -0
  73. {invocation_tree-0.0.29 → 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.29
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,14 +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
- You can use [configurations](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.
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.
524
526
 
525
- **solution exercise6:** 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.
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.
526
528
 
527
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.
528
530
 
@@ -538,6 +540,7 @@ It can be useful to hide certian variables or functions to avoid unnecessary com
538
540
 
539
541
  ```python
540
542
  tree = ivt.blocking()
543
+ tree.hide_vars.add('functionname.variablename') # or:
541
544
  tree.hide_vars.add('namespace.functionname.variablename')
542
545
  ```
543
546
 
@@ -545,6 +548,7 @@ Or hide certain function calls:
545
548
 
546
549
  ```python
547
550
  tree = ivt.blocking()
551
+ tree.hide_calls.add('functionname') # or:
548
552
  tree.hide_calls.add('namespace.functionname')
549
553
  ```
550
554
 
@@ -559,10 +563,10 @@ With the `re:` prefix we can use regular expresssions, for example:
559
563
 
560
564
  ```python
561
565
  tree = ivt.blocking()
562
- tree.ignore_calls.add('re:namespace\..*')
566
+ tree.ignore_calls.add(r're:namespace\..*')
563
567
  ```
564
568
 
565
- to hide all function of `namespace`.
569
+ ignores all function of `namespace`.
566
570
 
567
571
  ## Blocking ##
568
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,14 +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
- You can use [configurations](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.
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.
479
481
 
480
- **solution exercise6:** 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.
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.
481
483
 
482
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.
483
485
 
@@ -493,6 +495,7 @@ It can be useful to hide certian variables or functions to avoid unnecessary com
493
495
 
494
496
  ```python
495
497
  tree = ivt.blocking()
498
+ tree.hide_vars.add('functionname.variablename') # or:
496
499
  tree.hide_vars.add('namespace.functionname.variablename')
497
500
  ```
498
501
 
@@ -500,6 +503,7 @@ Or hide certain function calls:
500
503
 
501
504
  ```python
502
505
  tree = ivt.blocking()
506
+ tree.hide_calls.add('functionname') # or:
503
507
  tree.hide_calls.add('namespace.functionname')
504
508
  ```
505
509
 
@@ -514,10 +518,10 @@ With the `re:` prefix we can use regular expresssions, for example:
514
518
 
515
519
  ```python
516
520
  tree = ivt.blocking()
517
- tree.ignore_calls.add('re:namespace\..*')
521
+ tree.ignore_calls.add(r're:namespace\..*')
518
522
  ```
519
523
 
520
- to hide all function of `namespace`.
524
+ ignores all function of `namespace`.
521
525
 
522
526
  ## Blocking ##
523
527
 
@@ -9,7 +9,7 @@ import difflib
9
9
 
10
10
  import invocation_tree.regex_set as regset
11
11
 
12
- __version__ = "0.0.29"
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invocation_tree
3
- Version: 0.0.29
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,14 +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
- You can use [configurations](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.
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.
524
526
 
525
- **solution exercise6:** 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.
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.
526
528
 
527
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.
528
530
 
@@ -538,6 +540,7 @@ It can be useful to hide certian variables or functions to avoid unnecessary com
538
540
 
539
541
  ```python
540
542
  tree = ivt.blocking()
543
+ tree.hide_vars.add('functionname.variablename') # or:
541
544
  tree.hide_vars.add('namespace.functionname.variablename')
542
545
  ```
543
546
 
@@ -545,6 +548,7 @@ Or hide certain function calls:
545
548
 
546
549
  ```python
547
550
  tree = ivt.blocking()
551
+ tree.hide_calls.add('functionname') # or:
548
552
  tree.hide_calls.add('namespace.functionname')
549
553
  ```
550
554
 
@@ -559,10 +563,10 @@ With the `re:` prefix we can use regular expresssions, for example:
559
563
 
560
564
  ```python
561
565
  tree = ivt.blocking()
562
- tree.ignore_calls.add('re:namespace\..*')
566
+ tree.ignore_calls.add(r're:namespace\..*')
563
567
  ```
564
568
 
565
- to hide all function of `namespace`.
569
+ ignores all function of `namespace`.
566
570
 
567
571
  ## Blocking ##
568
572
 
@@ -2,21 +2,6 @@ LICENSE.txt
2
2
  MANIFEST.in
3
3
  README.md
4
4
  pyproject.toml
5
- images/bdsmlr-123210-XatifIR7GC.jpg
6
- images/bdsmlr-137085-6shuc85rzp.jpg
7
- images/bdsmlr-137085-BgIaVabSuO.jpg
8
- images/bdsmlr-137085-EbOCN2t7pK.jpg
9
- images/bdsmlr-137085-G7VfslodUg.jpg
10
- images/bdsmlr-137085-bECkK7Tmrl.jpg
11
- images/bdsmlr-137085-dwKgKQW44r.jpg
12
- images/bdsmlr-137085-k3Cc6Y1Czs.jpg
13
- images/bdsmlr-137085-lG052p8RGk.jpg
14
- images/bdsmlr-13874-OSFOTjkp0j.jpg
15
- images/bdsmlr-193150-3cpmNpHLOn.jpg
16
- images/bdsmlr-193150-DTYC3stBX0.jpg
17
- images/bdsmlr-193150-FqlVlDXAoY.jpg
18
- images/bdsmlr-193150-hdbOxh4o7w.jpg
19
- images/bdsmlr-193150-i64En9zgET.jpg
20
5
  images/compute.gif
21
6
  images/compute.py
22
7
  images/create_gif.sh
@@ -63,7 +48,6 @@ images/vscode.png
63
48
  images/__pycache__/graph.cpython-313.pyc
64
49
  invocation_tree/__init__.py
65
50
  invocation_tree/regex_set.py
66
- invocation_tree/test_regex_fix.py
67
51
  invocation_tree.egg-info/PKG-INFO
68
52
  invocation_tree.egg-info/SOURCES.txt
69
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.29"
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})")