invocation-tree 0.0.33__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.
- {invocation_tree-0.0.33/invocation_tree.egg-info → invocation_tree-0.0.34}/PKG-INFO +28 -7
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/README.md +27 -6
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/invocation_tree/__init__.py +99 -9
- {invocation_tree-0.0.33 → invocation_tree-0.0.34/invocation_tree.egg-info}/PKG-INFO +28 -7
- invocation_tree-0.0.34/invocation_tree.egg-info/SOURCES.txt +10 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/pyproject.toml +1 -1
- invocation_tree-0.0.33/MANIFEST.in +0 -1
- invocation_tree-0.0.33/images/compute.gif +0 -0
- invocation_tree-0.0.33/images/compute.py +0 -22
- invocation_tree-0.0.33/images/create_gif.sh +0 -25
- invocation_tree-0.0.33/images/create_images.sh +0 -47
- invocation_tree-0.0.33/images/draw_graph.py +0 -32
- invocation_tree-0.0.33/images/draw_graph_d_x.py +0 -36
- invocation_tree-0.0.33/images/edges_big.out +0 -1
- invocation_tree-0.0.33/images/edges_big_d_x.out +0 -1
- invocation_tree-0.0.33/images/edges_small.out +0 -1
- invocation_tree-0.0.33/images/factorial.gif +0 -0
- invocation_tree-0.0.33/images/factorial.py +0 -9
- invocation_tree-0.0.33/images/graph.py +0 -35
- invocation_tree-0.0.33/images/graph_big.png +0 -0
- invocation_tree-0.0.33/images/graph_big_d_x.png +0 -0
- invocation_tree-0.0.33/images/graph_small.png +0 -0
- invocation_tree-0.0.33/images/jugs.png +0 -0
- invocation_tree-0.0.33/images/jugs_depth_first.py +0 -38
- invocation_tree-0.0.33/images/perms_LR3.png +0 -0
- invocation_tree-0.0.33/images/permutations.gif +0 -0
- invocation_tree-0.0.33/images/permutations.py +0 -11
- invocation_tree-0.0.33/images/permutations_collect.gif +0 -0
- invocation_tree-0.0.33/images/permutations_collect.py +0 -13
- invocation_tree-0.0.33/images/permutations_dot.py +0 -30
- invocation_tree-0.0.33/images/permutations_neighbor.gif +0 -0
- invocation_tree-0.0.33/images/permutations_neighbor.py +0 -12
- invocation_tree-0.0.33/images/permutations_return.gif +0 -0
- invocation_tree-0.0.33/images/permutations_return.py +0 -13
- invocation_tree-0.0.33/images/permutations_vscode.gif +0 -0
- invocation_tree-0.0.33/images/print_all_paths.py +0 -26
- invocation_tree-0.0.33/images/print_all_paths_of_length.py +0 -27
- invocation_tree-0.0.33/images/print_all_paths_via.py +0 -32
- invocation_tree-0.0.33/images/quick_sort.gif +0 -0
- invocation_tree-0.0.33/images/quick_sort.py +0 -16
- invocation_tree-0.0.33/images/students.gif +0 -0
- invocation_tree-0.0.33/images/students.py +0 -28
- invocation_tree-0.0.33/images/vscode.png +0 -0
- invocation_tree-0.0.33/invocation_tree.egg-info/SOURCES.txt +0 -47
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/LICENSE.txt +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/invocation_tree/regex_set.py +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/invocation_tree.egg-info/dependency_links.txt +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/invocation_tree.egg-info/requires.txt +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.34}/invocation_tree.egg-info/top_level.txt +0 -0
- {invocation_tree-0.0.33 → 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.
|
|
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
|
-
|
|
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 <Enter> 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 <Enter> to step through program execution.
|
|
137
137
|
|
|
138
138
|

|
|
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,
|
|
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
|
|
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
|

|
|
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:
|
|
@@ -47,7 +47,7 @@ ___
|
|
|
47
47
|
|
|
48
48
|
# Iteration and Recursion #
|
|
49
49
|
|
|
50
|
-
|
|
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 <Enter> 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 <Enter> to step through program execution.
|
|
117
117
|
|
|
118
118
|

|
|
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,
|
|
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
|
|
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
|

|
|
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:
|
|
@@ -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.
|
|
13
|
+
__version__ = "0.0.34"
|
|
13
14
|
__author__ = 'Bas Terwijn'
|
|
14
15
|
|
|
15
16
|
def highlight_diff(str1, str2):
|
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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 <Enter> 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 <Enter> to step through program execution.
|
|
137
137
|
|
|
138
138
|

|
|
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,
|
|
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
|
|
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
|

|
|
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:
|
|
@@ -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
|
|
@@ -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,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
|
|
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
|
|
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)
|
|
Binary file
|
|
@@ -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]))
|
|
Binary file
|
|
@@ -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
|
|
Binary file
|
|
@@ -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))
|
|
Binary file
|
|
@@ -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))
|
|
Binary file
|
|
@@ -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,47 +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
|
-
invocation_tree/__init__.py
|
|
42
|
-
invocation_tree/regex_set.py
|
|
43
|
-
invocation_tree.egg-info/PKG-INFO
|
|
44
|
-
invocation_tree.egg-info/SOURCES.txt
|
|
45
|
-
invocation_tree.egg-info/dependency_links.txt
|
|
46
|
-
invocation_tree.egg-info/requires.txt
|
|
47
|
-
invocation_tree.egg-info/top_level.txt
|
|
File without changes
|
|
File without changes
|
{invocation_tree-0.0.33 → invocation_tree-0.0.34}/invocation_tree.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|