invocation-tree 0.0.32__tar.gz → 0.0.34__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 (51) hide show
  1. {invocation_tree-0.0.32/invocation_tree.egg-info → invocation_tree-0.0.34}/PKG-INFO +30 -9
  2. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/README.md +29 -8
  3. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/invocation_tree/__init__.py +100 -10
  4. {invocation_tree-0.0.32 → invocation_tree-0.0.34/invocation_tree.egg-info}/PKG-INFO +30 -9
  5. invocation_tree-0.0.34/invocation_tree.egg-info/SOURCES.txt +10 -0
  6. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/pyproject.toml +1 -1
  7. invocation_tree-0.0.32/MANIFEST.in +0 -1
  8. invocation_tree-0.0.32/images/__pycache__/graph.cpython-313.pyc +0 -0
  9. invocation_tree-0.0.32/images/compute.gif +0 -0
  10. invocation_tree-0.0.32/images/compute.py +0 -22
  11. invocation_tree-0.0.32/images/create_gif.sh +0 -25
  12. invocation_tree-0.0.32/images/create_images.sh +0 -47
  13. invocation_tree-0.0.32/images/draw_graph.py +0 -32
  14. invocation_tree-0.0.32/images/draw_graph_d_x.py +0 -36
  15. invocation_tree-0.0.32/images/edges_big.out +0 -1
  16. invocation_tree-0.0.32/images/edges_big_d_x.out +0 -1
  17. invocation_tree-0.0.32/images/edges_small.out +0 -1
  18. invocation_tree-0.0.32/images/factorial.gif +0 -0
  19. invocation_tree-0.0.32/images/factorial.py +0 -9
  20. invocation_tree-0.0.32/images/graph.py +0 -35
  21. invocation_tree-0.0.32/images/graph_big.png +0 -0
  22. invocation_tree-0.0.32/images/graph_big_d_x.png +0 -0
  23. invocation_tree-0.0.32/images/graph_small.png +0 -0
  24. invocation_tree-0.0.32/images/jugs.png +0 -0
  25. invocation_tree-0.0.32/images/jugs_depth_first.py +0 -38
  26. invocation_tree-0.0.32/images/perms_LR3.png +0 -0
  27. invocation_tree-0.0.32/images/permutations.gif +0 -0
  28. invocation_tree-0.0.32/images/permutations.py +0 -11
  29. invocation_tree-0.0.32/images/permutations_collect.gif +0 -0
  30. invocation_tree-0.0.32/images/permutations_collect.py +0 -13
  31. invocation_tree-0.0.32/images/permutations_dot.py +0 -30
  32. invocation_tree-0.0.32/images/permutations_neighbor.gif +0 -0
  33. invocation_tree-0.0.32/images/permutations_neighbor.py +0 -12
  34. invocation_tree-0.0.32/images/permutations_return.gif +0 -0
  35. invocation_tree-0.0.32/images/permutations_return.py +0 -13
  36. invocation_tree-0.0.32/images/permutations_vscode.gif +0 -0
  37. invocation_tree-0.0.32/images/print_all_paths.py +0 -26
  38. invocation_tree-0.0.32/images/print_all_paths_of_length.py +0 -27
  39. invocation_tree-0.0.32/images/print_all_paths_via.py +0 -32
  40. invocation_tree-0.0.32/images/quick_sort.gif +0 -0
  41. invocation_tree-0.0.32/images/quick_sort.py +0 -16
  42. invocation_tree-0.0.32/images/students.gif +0 -0
  43. invocation_tree-0.0.32/images/students.py +0 -28
  44. invocation_tree-0.0.32/images/vscode.png +0 -0
  45. invocation_tree-0.0.32/invocation_tree.egg-info/SOURCES.txt +0 -48
  46. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/LICENSE.txt +0 -0
  47. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/invocation_tree/regex_set.py +0 -0
  48. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/invocation_tree.egg-info/dependency_links.txt +0 -0
  49. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/invocation_tree.egg-info/requires.txt +0 -0
  50. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/invocation_tree.egg-info/top_level.txt +0 -0
  51. {invocation_tree-0.0.32 → invocation_tree-0.0.34}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invocation_tree
3
- Version: 0.0.32
3
+ Version: 0.0.34
4
4
  Summary: Generates an invocation tree of functions calls.
5
5
  Author-email: Bas Terwijn <bterwijn@gmail.com>
6
6
  License-Expression: BSD-2-Clause
@@ -67,7 +67,7 @@ ___
67
67
 
68
68
  # Iteration and Recursion #
69
69
 
70
- Repetion can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
70
+ Repetition can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
71
71
 
72
72
  ``` python
73
73
  import math
@@ -119,7 +119,7 @@ factorial(4) = 4 * factorial(3)
119
119
  = 24
120
120
  ```
121
121
 
122
- To better understand what is going on when we run the program we can use invocation_tree:
122
+ To better understand what is going on when we run the program, we can use invocation_tree:
123
123
 
124
124
  ```python
125
125
  import invocation_tree as ivt
@@ -133,7 +133,7 @@ tree = ivt.blocking() # block and wait for <Enter> key press
133
133
  tree(factorial, 4) # call function 'factorial' with argument '4'
134
134
  ```
135
135
 
136
- to graph the function invocations. Run this program and press &lt;Enter&gt; to step through program execution.
136
+ to graph the function invocations where function calls happen on the way down, and where results combine on the way up when a function returns. Run this program and press &lt;Enter&gt; to step through program execution.
137
137
 
138
138
  ![factorial](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/factorial.gif)
139
139
 
@@ -147,7 +147,7 @@ Each node in the invocation tree represents a function call, and the node's colo
147
147
 
148
148
  For every function call, the package displays its **local variables** and **return value**. Changes to the values of these variables over time are highlighted using bold text and gray shading to make them easier to track.
149
149
 
150
- With recursion we often use a divide and conquer strategy, spliting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
150
+ With recursion we often use a divide and conquer strategy, splitting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
151
151
 
152
152
  **exercise1:** Use recursions to compute the sum of all the values in a list (hint: split for example the list `[1, 2, 3, ...]` in head `1` and tail `[2, 3, ...]`).
153
153
  ```python
@@ -170,7 +170,7 @@ def binary(decimal):
170
170
  print( binary(22) ) # [1, 0, 1, 1, 0]
171
171
  ```
172
172
 
173
- Checking that `[1, 0, 1, 1, 0]` is the correct binary repsentation for decimal 22:
173
+ Checking that `[1, 0, 1, 1, 0]` is the correct binary representation for decimal 22:
174
174
 
175
175
  | | 2<sup>4</sup> | 2<sup>3</sup> | 2<sup>2</sup> | 2<sup>1</sup> | 2<sup>0</sup> | |
176
176
  |------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|
@@ -187,7 +187,7 @@ We can use recursion to compute all permutation of a number of elements with rep
187
187
 
188
188
  ![perms_LR3](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/perms_LR3.png)
189
189
 
190
- This can be implemented recursively like:
190
+ This can be implemented recursively, using a divide and conquer strategy, like:
191
191
 
192
192
  ```python
193
193
  import invocation_tree as ivt
@@ -510,6 +510,7 @@ $ python jugs_breadth_first.py 51 3,5,34,107
510
510
  # Configuration #
511
511
  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).
512
512
 
513
+
513
514
  ## Hidding ##
514
515
  It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
515
516
 
@@ -543,6 +544,26 @@ tree.ignore_calls.add(r're:namespace\..*')
543
544
 
544
545
  ignores all function of `namespace`.
545
546
 
547
+ ## Decorator ##
548
+
549
+ A better way to hide functions is to use the `@ivt.show` decorator on only the functions you want to graph. The decorator uses the global `ivt.decorator_tree`.
550
+
551
+ ```python
552
+ import invocation_tree as ivt
553
+
554
+ ivt.decorator_tree = ivt.blocking() # set tree used by decorator
555
+
556
+ @ivt.show # use decorator to select which functions to graph
557
+ def permutations(elements, perm, n):
558
+ if n == 0:
559
+ print(perm)
560
+ else:
561
+ for element in elements:
562
+ permutations(elements, perm + '\n' + element, n-1)
563
+
564
+ permutations( 'LR', '', 3) # all permutations of L and R of length 3
565
+ ```
566
+
546
567
  ## Blocking ##
547
568
 
548
569
  For convenience we provide these functions to set common configurations:
@@ -602,9 +623,9 @@ tree = ivt.Invocation_Tree()
602
623
  - **tree.ignore_calls** : set()
603
624
  - set of all functions names that are not shown in the tree, including its children
604
625
  - **tree.fontname** : str
605
- - the font used in the graph, default 'Courier' (as widely available monospace).
626
+ - the font used in the graph, default 'Times-Roman' (widely available on the web)
606
627
  - **tree.fontsize** : str
607
- - the font size used in the graph, default '14'.
628
+ - the font size used in the graph, default '14'
608
629
 
609
630
  # Troubleshooting #
610
631
  - Adobe Acrobat Reader [doesn't refresh a PDF file](https://community.adobe.com/t5/acrobat-reader-discussions/reload-refresh-pdfs/td-p/9632292) when it changes on disk and blocks updates which results in an `Could not open 'tree.pdf' for writing : Permission denied` error. One solution is to install a PDF reader that does refresh ([SumatraPDF](https://www.sumatrapdfreader.org/), [Okular](https://okular.kde.org/), ...) and set it as the default PDF reader. Another solution is to `render()` the graph to a different output format.
@@ -47,7 +47,7 @@ ___
47
47
 
48
48
  # Iteration and Recursion #
49
49
 
50
- Repetion can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
50
+ Repetition 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
@@ -99,7 +99,7 @@ factorial(4) = 4 * factorial(3)
99
99
  = 24
100
100
  ```
101
101
 
102
- To better understand what is going on when we run the program we can use invocation_tree:
102
+ To better understand what is going on when we run the program, we can use invocation_tree:
103
103
 
104
104
  ```python
105
105
  import invocation_tree as ivt
@@ -113,7 +113,7 @@ tree = ivt.blocking() # block and wait for <Enter> key press
113
113
  tree(factorial, 4) # call function 'factorial' with argument '4'
114
114
  ```
115
115
 
116
- to graph the function invocations. Run this program and press &lt;Enter&gt; to step through program execution.
116
+ to graph the function invocations where function calls happen on the way down, and where results combine on the way up when a function returns. Run this program and press &lt;Enter&gt; to step through program execution.
117
117
 
118
118
  ![factorial](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/factorial.gif)
119
119
 
@@ -127,7 +127,7 @@ Each node in the invocation tree represents a function call, and the node's colo
127
127
 
128
128
  For every function call, the package displays its **local variables** and **return value**. Changes to the values of these variables over time are highlighted using bold text and gray shading to make them easier to track.
129
129
 
130
- With recursion we often use a divide and conquer strategy, spliting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
130
+ With recursion we often use a divide and conquer strategy, splitting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
131
131
 
132
132
  **exercise1:** Use recursions to compute the sum of all the values in a list (hint: split for example the list `[1, 2, 3, ...]` in head `1` and tail `[2, 3, ...]`).
133
133
  ```python
@@ -150,7 +150,7 @@ def binary(decimal):
150
150
  print( binary(22) ) # [1, 0, 1, 1, 0]
151
151
  ```
152
152
 
153
- Checking that `[1, 0, 1, 1, 0]` is the correct binary repsentation for decimal 22:
153
+ Checking that `[1, 0, 1, 1, 0]` is the correct binary representation for decimal 22:
154
154
 
155
155
  | | 2<sup>4</sup> | 2<sup>3</sup> | 2<sup>2</sup> | 2<sup>1</sup> | 2<sup>0</sup> | |
156
156
  |------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|
@@ -167,7 +167,7 @@ We can use recursion to compute all permutation of a number of elements with rep
167
167
 
168
168
  ![perms_LR3](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/perms_LR3.png)
169
169
 
170
- This can be implemented recursively like:
170
+ This can be implemented recursively, using a divide and conquer strategy, like:
171
171
 
172
172
  ```python
173
173
  import invocation_tree as ivt
@@ -490,6 +490,7 @@ $ python jugs_breadth_first.py 51 3,5,34,107
490
490
  # Configuration #
491
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).
492
492
 
493
+
493
494
  ## Hidding ##
494
495
  It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
495
496
 
@@ -523,6 +524,26 @@ tree.ignore_calls.add(r're:namespace\..*')
523
524
 
524
525
  ignores all function of `namespace`.
525
526
 
527
+ ## Decorator ##
528
+
529
+ A better way to hide functions is to use the `@ivt.show` decorator on only the functions you want to graph. The decorator uses the global `ivt.decorator_tree`.
530
+
531
+ ```python
532
+ import invocation_tree as ivt
533
+
534
+ ivt.decorator_tree = ivt.blocking() # set tree used by decorator
535
+
536
+ @ivt.show # use decorator to select which functions to graph
537
+ def permutations(elements, perm, n):
538
+ if n == 0:
539
+ print(perm)
540
+ else:
541
+ for element in elements:
542
+ permutations(elements, perm + '\n' + element, n-1)
543
+
544
+ permutations( 'LR', '', 3) # all permutations of L and R of length 3
545
+ ```
546
+
526
547
  ## Blocking ##
527
548
 
528
549
  For convenience we provide these functions to set common configurations:
@@ -582,9 +603,9 @@ tree = ivt.Invocation_Tree()
582
603
  - **tree.ignore_calls** : set()
583
604
  - set of all functions names that are not shown in the tree, including its children
584
605
  - **tree.fontname** : str
585
- - the font used in the graph, default 'Courier' (as widely available monospace).
606
+ - the font used in the graph, default 'Times-Roman' (widely available on the web)
586
607
  - **tree.fontsize** : str
587
- - the font size used in the graph, default '14'.
608
+ - the font size used in the graph, default '14'
588
609
 
589
610
  # Troubleshooting #
590
611
  - Adobe Acrobat Reader [doesn't refresh a PDF file](https://community.adobe.com/t5/acrobat-reader-discussions/reload-refresh-pdfs/td-p/9632292) when it changes on disk and blocks updates which results in an `Could not open 'tree.pdf' for writing : Permission denied` error. One solution is to install a PDF reader that does refresh ([SumatraPDF](https://www.sumatrapdfreader.org/), [Okular](https://okular.kde.org/), ...) and set it as the default PDF reader. Another solution is to `render()` the graph to a different output format.
@@ -6,10 +6,11 @@ from graphviz import Digraph
6
6
  import html
7
7
  import sys
8
8
  import difflib
9
+ import functools
9
10
 
10
11
  import invocation_tree.regex_set as regset
11
12
 
12
- __version__ = "0.0.32"
13
+ __version__ = "0.0.34"
13
14
  __author__ = 'Bas Terwijn'
14
15
 
15
16
  def highlight_diff(str1, str2):
@@ -106,7 +107,7 @@ class Invocation_Tree:
106
107
  self.regset_hide_vars = regset.Regex_Set(self.hide_vars)
107
108
  self.regset_hide_calls = regset.Regex_Set(self.hide_calls)
108
109
  self.regset_ignore_calls = regset.Regex_Set(self.ignore_calls)
109
- self.fontname = 'Courier'
110
+ self.fontname = 'Times-Roman'
110
111
  self.fontsize = '14'
111
112
  # --- core
112
113
  self.stack = []
@@ -134,7 +135,7 @@ class Invocation_Tree:
134
135
  sys.settrace(self.prev_global_tracer)
135
136
  return result
136
137
 
137
- def value_to_string(self, key, value, use_repr=False):
138
+ def value_to_string(self, key, value):
138
139
  try:
139
140
  if id(value) in self.to_string:
140
141
  val_str = self.to_string[id(value)](value)
@@ -143,18 +144,20 @@ class Invocation_Tree:
143
144
  elif type(value) in self.to_string:
144
145
  val_str = self.to_string[type(value)](value)
145
146
  else:
146
- val_str = repr(value) if use_repr else str(value)
147
+ val_str = str(value)
147
148
  except Exception as e:
148
149
  val_str = '<not-string-convertable>'
149
150
  if len(val_str) > self.max_string_len:
150
151
  val_str = '...'+val_str[-self.max_string_len:]
151
- return html.escape(val_str)
152
-
153
- def get_hightlighted_content(self, tree_node, key, value, use_old_content=False, use_repr=False):
152
+ result = html.escape(val_str)
153
+ result = result.replace('\n', '<BR/>') # use HTML line breaks
154
+ return result
155
+
156
+ def get_hightlighted_content(self, tree_node, key, value, use_old_content=False):
154
157
  if use_old_content and key in tree_node.strings:
155
158
  return tree_node.strings[key]
156
159
  is_highlighted = False
157
- content = self.value_to_string(key, value, use_repr=use_repr)
160
+ content = self.value_to_string(key, value)
158
161
  if key in tree_node.strings:
159
162
  use_old_content = tree_node.strings[key]
160
163
  hightlighted_content, is_highlighted = highlight_diff(use_old_content, content)
@@ -192,14 +195,14 @@ class Invocation_Tree:
192
195
  if filter_variables(var,val) and not self.regset_hide_vars.match(val_name, self.hide_vars):
193
196
  table += '</TR>\n <TR>'
194
197
  hightlighted_var = self.get_hightlighted_content(tree_node, var_name, var, use_old_content)
195
- hightlighted_val = self.get_hightlighted_content(tree_node, val_name, val, use_old_content, use_repr=True)
198
+ hightlighted_val = self.get_hightlighted_content(tree_node, val_name, val, use_old_content)
196
199
  hightlighted_content = self.indent + hightlighted_var + ': ' + hightlighted_val
197
200
  table += '<TD ALIGN="left">'+ hightlighted_content +'</TD>'
198
201
  if is_returned:
199
202
  return_name = class_fun_name+'.return'
200
203
  if not self.regset_hide_vars.match(return_name, self.hide_vars):
201
204
  table += '</TR>\n <TR>'
202
- hightlighted_content = self.get_hightlighted_content(tree_node, return_name, return_value, use_old_content, use_repr=True)
205
+ hightlighted_content = self.get_hightlighted_content(tree_node, return_name, return_value, use_old_content)
203
206
  table += '<TD ALIGN="left">'+ 'return ' + hightlighted_content +'</TD>'
204
207
  table += '</TR>\n</TABLE>>'
205
208
  return table
@@ -349,6 +352,11 @@ class Invocation_Tree:
349
352
  return None # stop tracing if debugger stopped tracing
350
353
  return local_multiplexer
351
354
 
355
+ # Optimize: disable line tracing if not needed
356
+ if not self.each_line:
357
+ frame.f_trace_lines = False
358
+ frame.f_trace_opcodes = False
359
+
352
360
  return local_multiplexer
353
361
 
354
362
  def blocking(filename='tree.pdf'):
@@ -371,3 +379,85 @@ def gif_each_change(filename='tree.png'):
371
379
 
372
380
  def non_blocking(filename='tree.pdf'):
373
381
  return Invocation_Tree(filename=filename, block=False)
382
+
383
+
384
+ # ------ decorator ------
385
+
386
+ decorator_tree = None
387
+
388
+ def show(fun):
389
+ """ decorate a function with @ivt.show to visualize its invocation tree """
390
+ target_code = fun.__code__
391
+
392
+ @functools.wraps(fun)
393
+ def wrapper_ivt_deco(*args, **kwargs):
394
+ global decorator_tree
395
+ if decorator_tree is None:
396
+ return fun(*args, **kwargs)
397
+ prev_tracer = sys.gettrace()
398
+ active_depth = 0 # handles recursion of fun
399
+
400
+ def tracer(frame, event, arg):
401
+ nonlocal active_depth
402
+ # Check if this is our target function
403
+ if frame.f_code is target_code:
404
+ if event == "call":
405
+ active_depth += 1
406
+ if active_depth == 1:
407
+ decorator_tree.trace(frame, event, None)
408
+ # Optimize: disable line tracing if not needed
409
+ if not getattr(decorator_tree, "each_line", False):
410
+ frame.f_trace_lines = False
411
+ frame.f_trace_opcodes = False
412
+ elif event == "line":
413
+ if active_depth == 1 and getattr(decorator_tree, "each_line", False):
414
+ decorator_tree.trace(frame, event, None)
415
+ elif event == "return":
416
+ if active_depth == 1:
417
+ decorator_tree.trace(frame, event, arg)
418
+ active_depth -= 1
419
+ return tracer
420
+
421
+ # Not our target: chain to previous tracer or return None to stop tracing this frame
422
+ if prev_tracer is not None:
423
+ return prev_tracer(frame, event, arg)
424
+
425
+ return None # Don't trace non-target frames
426
+
427
+ sys.settrace(tracer)
428
+ try:
429
+ return fun(*args, **kwargs)
430
+ finally:
431
+ sys.settrace(prev_tracer)
432
+ return wrapper_ivt_deco
433
+
434
+ # faster decorator but no 'line' events
435
+ def decorate_profile(fun):
436
+ target_code = fun.__code__
437
+
438
+ @functools.wraps(fun)
439
+ def wrapper_ivt_deco(*args, **kwargs):
440
+ global decorator_tree
441
+ if decorator_tree is None:
442
+ return fun(*args, **kwargs)
443
+ prev_profiler = sys.getprofile()
444
+ active_depth = 0 # handles recursion of fun
445
+
446
+ def profiler(frame, event, arg):
447
+ nonlocal active_depth
448
+ if frame.f_code is target_code:
449
+ if event == "call":
450
+ active_depth += 1
451
+ if active_depth == 1:
452
+ decorator_tree.trace(frame, event, None)
453
+ elif event == "return":
454
+ if active_depth == 1:
455
+ decorator_tree.trace(frame, event, arg)
456
+ active_depth -= 1
457
+
458
+ sys.setprofile(profiler)
459
+ try:
460
+ return fun(*args, **kwargs)
461
+ finally:
462
+ sys.setprofile(prev_profiler)
463
+ return wrapper_ivt_deco
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invocation_tree
3
- Version: 0.0.32
3
+ Version: 0.0.34
4
4
  Summary: Generates an invocation tree of functions calls.
5
5
  Author-email: Bas Terwijn <bterwijn@gmail.com>
6
6
  License-Expression: BSD-2-Clause
@@ -67,7 +67,7 @@ ___
67
67
 
68
68
  # Iteration and Recursion #
69
69
 
70
- Repetion can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
70
+ Repetition can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
71
71
 
72
72
  ``` python
73
73
  import math
@@ -119,7 +119,7 @@ factorial(4) = 4 * factorial(3)
119
119
  = 24
120
120
  ```
121
121
 
122
- To better understand what is going on when we run the program we can use invocation_tree:
122
+ To better understand what is going on when we run the program, we can use invocation_tree:
123
123
 
124
124
  ```python
125
125
  import invocation_tree as ivt
@@ -133,7 +133,7 @@ tree = ivt.blocking() # block and wait for <Enter> key press
133
133
  tree(factorial, 4) # call function 'factorial' with argument '4'
134
134
  ```
135
135
 
136
- to graph the function invocations. Run this program and press &lt;Enter&gt; to step through program execution.
136
+ to graph the function invocations where function calls happen on the way down, and where results combine on the way up when a function returns. Run this program and press &lt;Enter&gt; to step through program execution.
137
137
 
138
138
  ![factorial](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/factorial.gif)
139
139
 
@@ -147,7 +147,7 @@ Each node in the invocation tree represents a function call, and the node's colo
147
147
 
148
148
  For every function call, the package displays its **local variables** and **return value**. Changes to the values of these variables over time are highlighted using bold text and gray shading to make them easier to track.
149
149
 
150
- With recursion we often use a divide and conquer strategy, spliting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
150
+ With recursion we often use a divide and conquer strategy, splitting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
151
151
 
152
152
  **exercise1:** Use recursions to compute the sum of all the values in a list (hint: split for example the list `[1, 2, 3, ...]` in head `1` and tail `[2, 3, ...]`).
153
153
  ```python
@@ -170,7 +170,7 @@ def binary(decimal):
170
170
  print( binary(22) ) # [1, 0, 1, 1, 0]
171
171
  ```
172
172
 
173
- Checking that `[1, 0, 1, 1, 0]` is the correct binary repsentation for decimal 22:
173
+ Checking that `[1, 0, 1, 1, 0]` is the correct binary representation for decimal 22:
174
174
 
175
175
  | | 2<sup>4</sup> | 2<sup>3</sup> | 2<sup>2</sup> | 2<sup>1</sup> | 2<sup>0</sup> | |
176
176
  |------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|
@@ -187,7 +187,7 @@ We can use recursion to compute all permutation of a number of elements with rep
187
187
 
188
188
  ![perms_LR3](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/perms_LR3.png)
189
189
 
190
- This can be implemented recursively like:
190
+ This can be implemented recursively, using a divide and conquer strategy, like:
191
191
 
192
192
  ```python
193
193
  import invocation_tree as ivt
@@ -510,6 +510,7 @@ $ python jugs_breadth_first.py 51 3,5,34,107
510
510
  # Configuration #
511
511
  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).
512
512
 
513
+
513
514
  ## Hidding ##
514
515
  It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
515
516
 
@@ -543,6 +544,26 @@ tree.ignore_calls.add(r're:namespace\..*')
543
544
 
544
545
  ignores all function of `namespace`.
545
546
 
547
+ ## Decorator ##
548
+
549
+ A better way to hide functions is to use the `@ivt.show` decorator on only the functions you want to graph. The decorator uses the global `ivt.decorator_tree`.
550
+
551
+ ```python
552
+ import invocation_tree as ivt
553
+
554
+ ivt.decorator_tree = ivt.blocking() # set tree used by decorator
555
+
556
+ @ivt.show # use decorator to select which functions to graph
557
+ def permutations(elements, perm, n):
558
+ if n == 0:
559
+ print(perm)
560
+ else:
561
+ for element in elements:
562
+ permutations(elements, perm + '\n' + element, n-1)
563
+
564
+ permutations( 'LR', '', 3) # all permutations of L and R of length 3
565
+ ```
566
+
546
567
  ## Blocking ##
547
568
 
548
569
  For convenience we provide these functions to set common configurations:
@@ -602,9 +623,9 @@ tree = ivt.Invocation_Tree()
602
623
  - **tree.ignore_calls** : set()
603
624
  - set of all functions names that are not shown in the tree, including its children
604
625
  - **tree.fontname** : str
605
- - the font used in the graph, default 'Courier' (as widely available monospace).
626
+ - the font used in the graph, default 'Times-Roman' (widely available on the web)
606
627
  - **tree.fontsize** : str
607
- - the font size used in the graph, default '14'.
628
+ - the font size used in the graph, default '14'
608
629
 
609
630
  # Troubleshooting #
610
631
  - Adobe Acrobat Reader [doesn't refresh a PDF file](https://community.adobe.com/t5/acrobat-reader-discussions/reload-refresh-pdfs/td-p/9632292) when it changes on disk and blocks updates which results in an `Could not open 'tree.pdf' for writing : Permission denied` error. One solution is to install a PDF reader that does refresh ([SumatraPDF](https://www.sumatrapdfreader.org/), [Okular](https://okular.kde.org/), ...) and set it as the default PDF reader. Another solution is to `render()` the graph to a different output format.
@@ -0,0 +1,10 @@
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ invocation_tree/__init__.py
5
+ invocation_tree/regex_set.py
6
+ invocation_tree.egg-info/PKG-INFO
7
+ invocation_tree.egg-info/SOURCES.txt
8
+ invocation_tree.egg-info/dependency_links.txt
9
+ invocation_tree.egg-info/requires.txt
10
+ invocation_tree.egg-info/top_level.txt
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "invocation_tree"
7
- version = "0.0.32"
7
+ version = "0.0.34"
8
8
  description = "Generates an invocation tree of functions calls."
9
9
  authors = [
10
10
  {name = "Bas Terwijn", email = "bterwijn@gmail.com"}
@@ -1 +0,0 @@
1
- recursive-include images/ *
Binary file
@@ -1,22 +0,0 @@
1
- import invocation_tree as ivt
2
-
3
- def main():
4
- a = 1
5
- a = expression(a)
6
- return multiply(a, 6)
7
-
8
- def expression(a):
9
- a = subtract(a, 3)
10
- return add(a, 9)
11
-
12
- def subtract(a, b):
13
- return a - b
14
-
15
- def add(a, b):
16
- return a + b
17
-
18
- def multiply(a, b):
19
- return a * b
20
-
21
- tree = ivt.gif('compute.png')
22
- print( tree(main) )
@@ -1,25 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # install:
4
- #
5
- # sudo apt install imagemagick
6
-
7
- name="$1"
8
- files=$(ls -v $name*.png | tr '\n' ' ')
9
- echo "creating gif with:"
10
- echo "$files"
11
-
12
- largest_size=$(identify -format "%H %Wx%H %f\n" $name*.png | sort -nr | head -n1| awk '{print $2}')
13
- echo "largest_size: $largest_size"
14
-
15
- echo "resizing images"
16
- mogrify -resize $largest_size -background white -gravity North -extent $largest_size $files
17
- echo "creating file: $name.gif"
18
- convert -delay 150 -dither None -loop 0 $files $name.gif
19
-
20
- if [ "$2" = "-d" ]; then
21
- echo "deleting: $files"
22
- rm $files
23
- fi
24
-
25
- echo "done"
@@ -1,47 +0,0 @@
1
-
2
- #rm -f compute*.png
3
- #python compute.py
4
- #rm -f compute0.png
5
- #bash create_gif.sh compute -d
6
-
7
- #rm -f students*.png
8
- #python students.py
9
- #rm -f students0.png
10
- #bash create_gif.sh students -d
11
-
12
- rm -f factorial*.png
13
- python factorial.py
14
- rm -f factorial0.png
15
- bash create_gif.sh factorial -d
16
-
17
- python permutations_dot.py perms_LR3.png LR 3
18
-
19
- rm -f permutations*.png
20
- python permutations.py
21
- rm -f permutations0.png
22
- bash create_gif.sh permutations -d
23
-
24
- rm -f permutations_neighbor*.png
25
- python permutations_neighbor.py
26
- rm -f permutations_neighbor0.png
27
- bash create_gif.sh permutations_neighbor -d
28
-
29
- python draw_graph.py 10 2 graph_small 2 > edges_small.out
30
- python draw_graph.py 26 3 graph_big 1 > edges_big.out
31
- python draw_graph_d_x.py 26 3 graph_big_d_x 1 > edges_big_d_x.out
32
-
33
- rm -f permutations_return*.png
34
- python permutations_return.py
35
- rm -f permutations_return0.png
36
- bash create_gif.sh permutations_return -d
37
-
38
- rm -f permutations_collect*.png
39
- python permutations_collect.py
40
- rm -f permutations_collect0.png
41
- bash create_gif.sh permutations_collect -d
42
-
43
- rm -f quick_sort*.png
44
- python quick_sort.py
45
- rm -f quick_sort0.png
46
- bash create_gif.sh quick_sort -d
47
-
@@ -1,32 +0,0 @@
1
- import sys
2
- import graph
3
- import graphviz
4
-
5
- def draw_graph(nodes, edges, filename='graph.png'):
6
- dot = graphviz.Graph(
7
- format='png',
8
- )
9
- for node in nodes:
10
- # Color specific nodes
11
- if node == 'a':
12
- dot.node(node, fillcolor='green', style='filled')
13
- elif node == 'b':
14
- dot.node(node, fillcolor='red', style='filled')
15
- else:
16
- dot.node(node)
17
- for edge in edges:
18
- dot.edge(edge[0], edge[1])
19
- dot.render(filename, cleanup=True)
20
- print('edges = ', edges)
21
-
22
- if __name__ == "__main__":
23
- if len(sys.argv) != 5:
24
- print("Usage: python draw_graph.py <n> <max_edges> <output_file> <seed>")
25
- sys.exit(1)
26
- n = int(sys.argv[1])
27
- max_edges = int(sys.argv[2])
28
- output_file = sys.argv[3]
29
- seed = int(sys.argv[4])
30
-
31
- nodes, edges = graph.generate(n, max_edges, seed)
32
- draw_graph(nodes, edges, output_file)
@@ -1,36 +0,0 @@
1
- import sys
2
- import graph
3
- import graphviz
4
-
5
- def draw_graph(nodes, edges, filename='graph.png'):
6
- dot = graphviz.Graph(
7
- format='png',
8
- )
9
- for node in nodes:
10
- # Color specific nodes
11
- if node == 'a':
12
- dot.node(node, fillcolor='green', style='filled')
13
- elif node == 'b':
14
- dot.node(node, fillcolor='red', style='filled')
15
- elif node == 'd':
16
- dot.node(node, fillcolor='orange', style='filled')
17
- elif node == 'x':
18
- dot.node(node, fillcolor='gray', style='filled')
19
- else:
20
- dot.node(node)
21
- for edge in edges:
22
- dot.edge(edge[0], edge[1])
23
- dot.render(filename, cleanup=True)
24
- print('edges = ', edges)
25
-
26
- if __name__ == "__main__":
27
- if len(sys.argv) != 5:
28
- print("Usage: python draw_graph.py <n> <max_edges> <output_file> <seed>")
29
- sys.exit(1)
30
- n = int(sys.argv[1])
31
- max_edges = int(sys.argv[2])
32
- output_file = sys.argv[3]
33
- seed = int(sys.argv[4])
34
-
35
- nodes, edges = graph.generate(n, max_edges, seed)
36
- draw_graph(nodes, edges, output_file)
@@ -1 +0,0 @@
1
- edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
@@ -1 +0,0 @@
1
- edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
@@ -1 +0,0 @@
1
- edges = [('a', 'j'), ('f', 'j'), ('c', 'e'), ('b', 'd'), ('b', 'e'), ('f', 'g'), ('g', 'i'), ('h', 'i'), ('e', 'h'), ('a', 'i'), ('b', 'h'), ('b', 'f')]
Binary file
@@ -1,9 +0,0 @@
1
- import invocation_tree as ivt
2
-
3
- def factorial(n):
4
- if n <= 1:
5
- return 1
6
- return n * factorial(n - 1)
7
-
8
- tree = ivt.gif('factorial.png')
9
- print(tree(factorial, 4))
@@ -1,35 +0,0 @@
1
- import random
2
- import string
3
-
4
- def get_name(i):
5
- name = ''
6
- while True:
7
- i, remainder = divmod(i, 26)
8
- name = string.ascii_lowercase[remainder] + name
9
- if i == 0:
10
- break
11
- i -= 1
12
- return name
13
-
14
- def generate(n, max_edge, seed=0):
15
- random.seed(seed)
16
- nodes = [get_name(i) for i in range(n)]
17
- if len(nodes) > 2:
18
- nodes[1], nodes[-1] = nodes[-1], nodes[1]
19
- edges = []
20
- for node1 in nodes:
21
- nr_edges = random.randint(1, max_edge)
22
- for _ in range(nr_edges):
23
- node2 = random.choice(nodes)
24
- if node1 != node2:
25
- if node1 > node2:
26
- node1 , node2 = node2, node1
27
- edge = (node1, node2)
28
- if edge not in edges:
29
- edges.append(edge)
30
- return nodes, edges
31
-
32
- if __name__ == '__main__':
33
- nodes, edges = generate(10, 3)
34
- print('nodes =', nodes)
35
- print('edges =', edges)
Binary file
Binary file
@@ -1,38 +0,0 @@
1
- import invocation_tree as ivt
2
- import jugs as jg
3
-
4
- def solver_depth_first(jugs, goal):
5
-
6
- def solver_depth_first_recursive(jugs, goal, jugs_hist, action_hist):
7
- actions = jugs.all_actions()
8
- for action in actions:
9
- goal_reached = jugs.do_action(action, goal)
10
- if jugs not in jugs_hist:
11
- jugs_hist.add(jugs.copy())
12
- action_hist.append(action)
13
- if goal_reached:
14
- return action_hist
15
- result = solver_depth_first_recursive(jugs, goal, jugs_hist, action_hist)
16
- if result:
17
- return result
18
- action_hist.pop()
19
- jugs_hist.remove(jugs)
20
- jugs.undo_action(action)
21
- return None
22
-
23
- jugs_hist = {jugs}
24
- action_hist = []
25
- return solver_depth_first_recursive(jugs.copy(), goal, jugs_hist, action_hist)
26
-
27
- if __name__ == '__main__':
28
- goal = 4
29
- print('Goal is to get a jug with', goal, 'liters')
30
- jugs = jg.Jugs((3, 5))
31
- print('We start with jugs:',jugs)
32
- tree = ivt.blocking()
33
- tree.hide_vars.add('solver_depth_first_recursive.jugs_hist')
34
- tree.hide_vars.add('solver_depth_first_recursive.action_hist')
35
- tree.ignore_calls.add('re:Jugs.*')
36
- solution_actions = tree(solver_depth_first, jugs, goal)
37
- #solution_actions = solver_depth_first( jugs, goal)
38
- jg.print_solution(jugs, solution_actions)
Binary file
@@ -1,11 +0,0 @@
1
- import invocation_tree as ivt
2
-
3
- def permutations(elements, perm, n):
4
- if n == 0:
5
- print(perm)
6
- else:
7
- for element in elements:
8
- permutations(elements, perm + element, n-1)
9
-
10
- tree = ivt.gif('permutations.png')
11
- result = tree(permutations, 'LR', '', 3)
@@ -1,13 +0,0 @@
1
- import invocation_tree as ivt
2
-
3
- def permutations(elements, perm, n, results):
4
- if n == 0:
5
- results.append(perm)
6
- else:
7
- for element in elements:
8
- permutations(elements, perm + element, n-1, results)
9
-
10
- tree = ivt.gif('permutations_collect.png')
11
- results = []
12
- tree(permutations, 'LR', '', 3, results)
13
- print(results)
@@ -1,30 +0,0 @@
1
- import sys
2
-
3
- from graphviz import Digraph
4
-
5
- def add_permute_nodes(dot, elements, perm, n, parent_id=None, node_id=[0]):
6
- if n < 0:
7
- return
8
-
9
- my_id = node_id[0]
10
- node_id[0] += 1
11
- dot.node(str(my_id), perm)
12
-
13
-
14
- if parent_id is not None:
15
- dot.edge(str(parent_id), str(my_id))
16
-
17
- for i, ch in enumerate(elements):
18
- add_permute_nodes(dot, elements, perm + ch, n-1, parent_id=my_id, node_id=node_id)
19
-
20
- def make_permutation_tree(outfile, elements, n):
21
- dot = Digraph()
22
- dot.attr(rankdir='TB') # top to bottom
23
- add_permute_nodes(dot, elements, "", n)
24
- splits = outfile.split('.')
25
- dot.format = splits[-1]
26
- dot.render(filename=''.join(splits[:-1]), cleanup=True)
27
- print(f"Generated graph: {outfile}")
28
-
29
- if __name__ == "__main__":
30
- make_permutation_tree(sys.argv[1], sys.argv[2], int(sys.argv[3]))
@@ -1,12 +0,0 @@
1
- import invocation_tree as ivt
2
-
3
- def permutations(elems, perm, n):
4
- if n == 0:
5
- print(perm)
6
- else:
7
- for element in elems:
8
- if len(perm) == 0 or not perm[-1] == element: # test neighbor
9
- permutations(elems, perm + element, n-1)
10
-
11
- tree = ivt.gif('permutations_neighbor.png')
12
- tree(permutations, 'ABC', '', 3) # permutations of A, B, C of length 3
@@ -1,13 +0,0 @@
1
- import invocation_tree as ivt
2
-
3
- def permutations(elements, perm, n):
4
- if n == 0:
5
- return [perm]
6
- else:
7
- results = []
8
- for element in elements:
9
- results += permutations(elements, perm + element, n-1)
10
- return results
11
-
12
- tree = ivt.gif('permutations_return.png')
13
- print(tree(permutations, 'LR', '', 3))
@@ -1,26 +0,0 @@
1
- edges = [('a', 'j'), ('f', 'j'), ('c', 'e'), ('b', 'd'), ('b', 'e'), ('f', 'g'), ('g', 'i'), ('h', 'i'), ('e', 'h'), ('a', 'i'), ('b', 'h'), ('b', 'f')]
2
-
3
- def edges_to_steps(edges: list[tuple[str, str]]) -> dict[str,list[str]]:
4
- """ Returns a dict with for each node the nodes it is connected with. """
5
- steps = {}
6
- for n1, n2 in edges:
7
- if not n1 in steps:
8
- steps[n1] = []
9
- steps[n1].append(n2)
10
- if not n2 in steps:
11
- steps[n2] = []
12
- steps[n2].append(n1)
13
- return steps
14
-
15
- def print_all_paths(steps, path, goal):
16
- current = path[-1]
17
- if current == goal:
18
- print(path)
19
- else:
20
- valid_steps = steps[current]
21
- for s in valid_steps:
22
- if s not in path:
23
- print_all_paths(steps, path+s, goal)
24
-
25
- steps = edges_to_steps(edges)
26
- print_all_paths(steps, 'a', 'b')
@@ -1,27 +0,0 @@
1
- edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
2
-
3
- def edges_to_steps(edges: list[tuple[str, str]]) -> dict[str,list[str]]:
4
- """ Returns a dict with for each node the nodes it is connected with. """
5
- steps = {}
6
- for n1, n2 in edges:
7
- if not n1 in steps:
8
- steps[n1] = []
9
- steps[n1].append(n2)
10
- if not n2 in steps:
11
- steps[n2] = []
12
- steps[n2].append(n1)
13
- return steps
14
-
15
- def print_all_paths(steps, path, goal, length):
16
- current = path[-1]
17
- length_path = len(path)
18
- if length_path >= length:
19
- if length_path == length and current == goal:
20
- print(path)
21
- else:
22
- valid_steps = steps[current]
23
- for s in valid_steps:
24
- print_all_paths(steps, path+s, goal, length)
25
-
26
- steps = edges_to_steps(edges)
27
- print_all_paths(steps, 'a', 'b', 7)
@@ -1,32 +0,0 @@
1
- edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
2
-
3
- def edges_to_steps(edges: list[tuple[str, str]]) -> dict[str,list[str]]:
4
- """ Returns a dict with for each node the nodes it is connected with. """
5
- steps = {}
6
- for n1, n2 in edges:
7
- if not n1 in steps:
8
- steps[n1] = []
9
- steps[n1].append(n2)
10
- if not n2 in steps:
11
- steps[n2] = []
12
- steps[n2].append(n1)
13
- return steps
14
-
15
- def print_all_paths(steps, path, goal, length, results):
16
- current = path[-1]
17
- length_path = len(path)
18
- if length_path >= length:
19
- if length_path == length and current == goal:
20
- if 'd' in path:
21
- results.append(path)
22
- else:
23
- valid_steps = steps[current]
24
- for s in valid_steps:
25
- if not s == 'x':
26
- print_all_paths(steps, path+s, goal, length, results)
27
-
28
- steps = edges_to_steps(edges)
29
- results = []
30
- print_all_paths(steps, 'a', 'b', 10, results)
31
- print('results:', results)
32
- print(len(results))
@@ -1,16 +0,0 @@
1
- import invocation_tree as ivt
2
-
3
- def quick_sort(values):
4
- if len(values) <= 1:
5
- return values
6
- pivot = values[0]
7
- smaller = [x for x in values if x < pivot]
8
- equal = [x for x in values if x == pivot]
9
- larger = [x for x in values if x > pivot]
10
- return quick_sort(smaller) + equal + quick_sort(larger)
11
-
12
- values = [7, 4, 10, 11, 2, 6, 9, 1, 5, 3, 8, 12]
13
- print('unsorted values:',values)
14
- tree = ivt.gif('quick_sort.png')
15
- values = tree(quick_sort, values)
16
- print(' sorted values:',values)
Binary file
@@ -1,28 +0,0 @@
1
- import invocation_tree as ivt
2
- from decimal import Decimal, ROUND_HALF_UP
3
-
4
- def main():
5
- students = {'Ann':[7.5, 8.0],
6
- 'Bob':[4.5, 6.0],
7
- 'Coy':[7.5, 6.0]}
8
- averages = {student:compute_average(grades)
9
- for student, grades in students.items()}
10
- passing = passing_students(averages)
11
- print(passing)
12
-
13
- def compute_average(grades):
14
- average = sum(grades)/len(grades)
15
- return half_up_round(average, 1)
16
-
17
- def half_up_round(value, digits=0):
18
- """ High-precision half-up rounding of 'value' to a specified number of 'digits'. """
19
- return float(Decimal(str(value)).quantize(Decimal(f"1e-{digits}"),
20
- rounding=ROUND_HALF_UP))
21
-
22
- def passing_students(avg):
23
- return [student
24
- for student, average in avg.items()
25
- if average >= 5.5]
26
-
27
- if __name__ == '__main__':
28
- ivt.gif(filename="students.png")(main)
Binary file
@@ -1,48 +0,0 @@
1
- LICENSE.txt
2
- MANIFEST.in
3
- README.md
4
- pyproject.toml
5
- images/compute.gif
6
- images/compute.py
7
- images/create_gif.sh
8
- images/create_images.sh
9
- images/draw_graph.py
10
- images/draw_graph_d_x.py
11
- images/edges_big.out
12
- images/edges_big_d_x.out
13
- images/edges_small.out
14
- images/factorial.gif
15
- images/factorial.py
16
- images/graph.py
17
- images/graph_big.png
18
- images/graph_big_d_x.png
19
- images/graph_small.png
20
- images/jugs.png
21
- images/jugs_depth_first.py
22
- images/perms_LR3.png
23
- images/permutations.gif
24
- images/permutations.py
25
- images/permutations_collect.gif
26
- images/permutations_collect.py
27
- images/permutations_dot.py
28
- images/permutations_neighbor.gif
29
- images/permutations_neighbor.py
30
- images/permutations_return.gif
31
- images/permutations_return.py
32
- images/permutations_vscode.gif
33
- images/print_all_paths.py
34
- images/print_all_paths_of_length.py
35
- images/print_all_paths_via.py
36
- images/quick_sort.gif
37
- images/quick_sort.py
38
- images/students.gif
39
- images/students.py
40
- images/vscode.png
41
- images/__pycache__/graph.cpython-313.pyc
42
- invocation_tree/__init__.py
43
- invocation_tree/regex_set.py
44
- invocation_tree.egg-info/PKG-INFO
45
- invocation_tree.egg-info/SOURCES.txt
46
- invocation_tree.egg-info/dependency_links.txt
47
- invocation_tree.egg-info/requires.txt
48
- invocation_tree.egg-info/top_level.txt