Implementation¶
-from functools import partial as curry
-from itertools import product
+from functools import partial as curry
+from itertools import product
ϕ and λ¶
The empty set and the set of just the empty string.
-phi = frozenset() # ϕ
+phi = frozenset() # ϕ
y = frozenset({''}) # λ
@@ -114,7 +115,7 @@ illustrate the algorithm and because you can represent any other
alphabet with two symbols (if you had to.)
I chose the names O and l (uppercase “o” and lowercase “L”) to
look like 0 and 1 (zero and one) respectively.
-syms = O, l = frozenset({'0'}), frozenset({'1'})
+syms = O, l = frozenset({'0'}), frozenset({'1'})
@@ -132,7 +133,7 @@ expression is one of:
Where R and S stand for regular expressions.
-AND, CONS, KSTAR, NOT, OR = 'and cons * not or'.split() # Tags are just strings.
+AND, CONS, KSTAR, NOT, OR = 'and cons * not or'.split() # Tags are just strings.
Because they are formed of frozenset, tuple and str objects
@@ -140,7 +141,7 @@ only, these datastructures are immutable.
String Representation of RE Datastructures¶
-def stringy(re):
+def stringy(re):
'''
Return a nice string repr for a regular expression datastructure.
'''
@@ -179,10 +180,10 @@ only, these datastructures are immutable.
I = (0|1)*
-I = (KSTAR, (OR, O, l))
+I = (KSTAR, (OR, O, l))
-print stringy(I)
+print stringy(I)
.
@@ -197,13 +198,13 @@ only, these datastructures are immutable.
Note that it contains one of everything.
-a = (CONS, I, (CONS, l, (CONS, l, (CONS, l, I))))
+a = (CONS, I, (CONS, l, (CONS, l, (CONS, l, I))))
b = (CONS, I, (CONS, O, l))
c = (CONS, l, (KSTAR, l))
it = (AND, a, (NOT, (OR, b, c)))
-print stringy(it)
+print stringy(it)
(.111.) & ((.01 | 11*)')
@@ -213,7 +214,7 @@ only, these datastructures are immutable.
nully()¶
Let’s get that auxiliary predicate function δ out of the way.
-def nully(R):
+def nully(R):
'''
δ - Return λ if λ ⊆ R otherwise ϕ.
'''
@@ -251,7 +252,7 @@ only, these datastructures are immutable.
This is the straightforward version with no “compaction”. It works fine,
but does waaaay too much work because the expressions grow each
derivation.
-def D(symbol):
+def D(symbol):
def derv(R):
@@ -295,7 +296,7 @@ derivation.
Compaction Rules¶
-def _compaction_rule(relation, one, zero, a, b):
+def _compaction_rule(relation, one, zero, a, b):
return (
b if a == one else # R*1 = 1*R = R
a if b == one else
@@ -305,7 +306,7 @@ derivation.
An elegant symmetry.
-# R ∧ I = I ∧ R = R
+# R ∧ I = I ∧ R = R
# R ∧ ϕ = ϕ ∧ R = ϕ
_and = curry(_compaction_rule, AND, I, phi)
@@ -324,14 +325,14 @@ derivation.
We can save re-processing by remembering results we have already
computed. RE datastructures are immutable and the derv() functions
are pure so this is fine.
-class Memo(object):
+class Memo(object):
- def __init__(self, f):
+ def __init__(self, f):
self.f = f
self.calls = self.hits = 0
self.mem = {}
- def __call__(self, key):
+ def __call__(self, key):
self.calls += 1
try:
result = self.mem[key]
@@ -346,7 +347,7 @@ are pure so this is fine.
With “Compaction”¶
This version uses the rules above to perform compaction. It keeps the
expressions from growing too large.
-def D_compaction(symbol):
+def D_compaction(symbol):
@Memo
def derv(R):
@@ -394,7 +395,7 @@ expressions from growing too large.
Let’s try it out…¶
(FIXME: redo.)
-o, z = D_compaction('0'), D_compaction('1')
+o, z = D_compaction('0'), D_compaction('1')
REs = set()
N = 5
names = list(product(*(N * [(0, 1)])))
@@ -498,8 +499,8 @@ machine transition table.
Says, “Three or more 1’s and not ending in 01 nor composed of all 1’s.”
-
-
omg.svg
+
+
omg.svg¶
Start at a and follow the transition arrows according to their
labels. Accepting states have a double outline. (Graphic generated with
@@ -552,18 +553,18 @@ a --1--> ∂1(a)
You can see the one-way nature of the g state and the hij “trap”
in the way that the .111. on the left-hand side of the &
disappears once it has been matched.
-from collections import defaultdict
-from pprint import pprint
-from string import ascii_lowercase
+from collections import defaultdict
+from pprint import pprint
+from string import ascii_lowercase
-d0, d1 = D_compaction('0'), D_compaction('1')
+d0, d1 = D_compaction('0'), D_compaction('1')
explore()¶
-def explore(re):
+def explore(re):
# Don't have more than 26 states...
names = defaultdict(iter(ascii_lowercase).next)
@@ -589,7 +590,7 @@ disappears once it has been matched.
return table, accepting
-table, accepting = explore(it)
+table, accepting = explore(it)
table
@@ -615,7 +616,7 @@ disappears once it has been matched.
('j', 1): 'h'}
-accepting
+accepting
{'h', 'i'}
@@ -626,7 +627,7 @@ disappears once it has been matched.
Generate Diagram¶
Once we have the FSM table and the set of accepting states we can
generate the diagram above.
-_template = '''\
+_template = '''\
digraph finite_state_machine {
rankdir=LR;
size="8,5"
@@ -650,7 +651,7 @@ generate the diagram above.
)
-print make_graph(table, accepting)
+print make_graph(table, accepting)
digraph finite_state_machine {
@@ -696,7 +697,7 @@ hard-code the information in the table into a little patch of branches.
Trampoline Function¶
Python has no GOTO statement but we can fake it with a “trampoline”
function.
-def trampoline(input_, jump_from, accepting):
+def trampoline(input_, jump_from, accepting):
I = iter(input_)
while True:
try:
@@ -711,7 +712,7 @@ function.
Stream Functions¶
Little helpers to process the iterator of our data (a “stream” of “1”
and “0” characters, not bits.)
-getch = lambda I: int(next(I))
+getch = lambda I: int(next(I))
def _1(I):
@@ -732,7 +733,7 @@ and “0” characters, not bits.)
code. (You have to imagine that these are GOTO statements in C or
branches in assembly and that the state names are branch destination
labels.)
-a = lambda I: c if getch(I) else b
+a = lambda I: c if getch(I) else b
b = lambda I: _0(I) or d
c = lambda I: e if getch(I) else b
d = lambda I: f if getch(I) else b
@@ -747,11 +748,11 @@ labels.)
Note that the implementations of h and g are identical ergo
h = g and we could eliminate one in the code but h is an
accepting state and g isn’t.
-def acceptable(input_):
+def acceptable(input_):
return trampoline(input_, a, {h, i})
-for n in range(2**5):
+for n in range(2**5):
s = bin(n)[2:]
print '%05s' % s, acceptable(s)
@@ -821,7 +822,7 @@ derivative-with-respect-to-N of some other state/RE:
b = d10(a)
-‘’‘
+‘’’
j = d1(d0(j))
@@ -842,46 +843,50 @@ derivative-with-respect-to-N of some other state/RE:
+
@@ -921,7 +925,7 @@ derivative-with-respect-to-N of some other state/RE:
Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/.
- Created using Sphinx 1.7.3.
+ Created using Sphinx 3.0.2.
diff --git a/docs/sphinx_docs/_build/html/notebooks/Developing.html b/docs/sphinx_docs/_build/html/notebooks/Developing.html
index 0e2fe6b..d10eab4 100644
--- a/docs/sphinx_docs/_build/html/notebooks/Developing.html
+++ b/docs/sphinx_docs/_build/html/notebooks/Developing.html
@@ -1,19 +1,18 @@
-
+
-
+
-
-
+
Developing a Program in Joy — Thun 0.2.0 documentation
-
-
-
-
-
+
+
+
+
+
+
@@ -30,6 +29,8 @@
+
+
@@ -41,17 +42,17 @@
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
-from notebook_preamble import J, V, define
+from notebook_preamble import J, V, define
Sum a range filtered by a predicate¶
Let’s create a predicate that returns True if a number is a multiple
of 3 or 5 and False otherwise.
-define('P == [3 % not] dupdip 5 % not or')
+define('P == [3 % not] dupdip 5 % not or')
-V('80 P')
+V('80 P')
. 80 P
@@ -114,10 +115,10 @@ the counter to the running sum. This function will do that:
PE1.1 == + [+] dupdip
-define('PE1.1 == + [+] dupdip')
+define('PE1.1 == + [+] dupdip')
-V('0 0 3 PE1.1')
+V('0 0 3 PE1.1')
. 0 0 3 PE1.1
@@ -132,7 +133,7 @@ the counter to the running sum. This function will do that:
3 3 .
-V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')
+V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')
. 0 0 [3 2 1 3 1 2 3] [PE1.1] step
@@ -215,26 +216,26 @@ total to 60.
How many multiples to sum?¶
-1000 / 15
+1000 / 15
66
-66 * 15
+66 * 15
990
-1000 - 990
+1000 - 990
10
We only want the terms less than 1000.
-999 - 990
+999 - 990
9
@@ -242,10 +243,10 @@ total to 60.
That means we want to run the full list of numbers sixty-six times to
get to 990 and then the first four numbers 3 2 1 3 to get to 999.
-define('PE1 == 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')
+define('PE1 == 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')
-J('PE1')
+J('PE1')
233168
@@ -265,16 +266,16 @@ integer terms from the list.
0b 11 10 01 11 01 10 11 == 14811
-0b11100111011011
+0b11100111011011
14811
-define('PE1.2 == [3 & PE1.1] dupdip 2 >>')
+define('PE1.2 == [3 & PE1.1] dupdip 2 >>')
-V('0 0 14811 PE1.2')
+V('0 0 14811 PE1.2')
. 0 0 14811 PE1.2
@@ -297,7 +298,7 @@ integer terms from the list.
3 3 3702 .
-V('3 3 3702 PE1.2')
+V('3 3 3702 PE1.2')
. 3 3 3702 PE1.2
@@ -320,7 +321,7 @@ integer terms from the list.
8 5 925 .
-V('0 0 14811 7 [PE1.2] times pop')
+V('0 0 14811 7 [PE1.2] times pop')
. 0 0 14811 7 [PE1.2] times pop
@@ -457,10 +458,10 @@ integer terms from the list.
And so we have at last:
-define('PE1 == 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')
+define('PE1 == 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')
-J('PE1')
+J('PE1')
233168
@@ -475,14 +476,14 @@ integer terms from the list.
n 14811 swap [PE1.2] times pop
-define('PE1.3 == 14811 swap [PE1.2] times pop')
+define('PE1.3 == 14811 swap [PE1.2] times pop')
Now we can simplify the definition above:
-define('PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop')
+define('PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop')
-J('PE1')
+J('PE1')
233168
@@ -505,10 +506,10 @@ I hope it’s clear.
then four more. In the Generator Programs notebook we derive a
generator that can be repeatedly driven by the x combinator to
produce a stream of the seven numbers repeating over and over again.
-define('PE1.terms == [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')
+define('PE1.terms == [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')
-J('PE1.terms 21 [x] times')
+J('PE1.terms 21 [x] times')
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]
@@ -516,19 +517,19 @@ produce a stream of the seven numbers repeating over and over again.
We know from above that we need sixty-six times seven then four more
terms to reach up to but not over one thousand.
-J('7 66 * 4 +')
+J('7 66 * 4 +')
466
-J('PE1.terms 466 [x] times pop')
+J('PE1.terms 466 [x] times pop')
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3
-J('[PE1.terms 466 [x] times pop] run sum')
+J('[PE1.terms 466 [x] times pop] run sum')
999
@@ -537,7 +538,7 @@ terms to reach up to but not over one thousand.
Now we can use PE1.1 to accumulate the terms as we go, and then
pop the generator and the counter from the stack when we’re done,
leaving just the sum.
-J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')
+J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')
233168
@@ -548,7 +549,7 @@ leaving just the sum.
A little further analysis renders iteration unnecessary.¶
Consider finding the sum of the positive integers less than or equal to
ten.
-J('[10 9 8 7 6 5 4 3 2 1] sum')
+J('[10 9 8 7 6 5 4 3 2 1] sum')
55
@@ -571,10 +572,10 @@ positive integers is:
(The formula also works for odd values of N, I’ll leave that to you if
you want to work it out or you can take my word for it.)
-define('F == dup ++ * 2 floordiv')
+define('F == dup ++ * 2 floordiv')
-V('10 F')
+V('10 F')
. 10 F
@@ -598,13 +599,13 @@ each, starting with:
If we reverse one of these two blocks and sum pairs…
-J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')
+J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')
[[978 15] [980 12] [981 10] [984 9] [985 6] [987 5] [990 3]]
-J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')
+J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')
[993 992 991 993 991 992 993]
@@ -612,7 +613,7 @@ each, starting with:
(Interesting that the sequence of seven numbers appears again in the
rightmost digit of each term.)
-J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')
+J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')
6945
@@ -626,7 +627,7 @@ additional unpaired terms between 990 and 1000:
So we can give the “sum of all the multiples of 3 or 5 below 1000” like
so:
-J('6945 33 * [993 995 996 999] cons sum')
+J('6945 33 * [993 995 996 999] cons sum')
233168
@@ -662,27 +663,50 @@ is just:
+
@@ -723,7 +746,7 @@ is just:
Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/.
- Created using Sphinx 1.7.3.
+ Created using Sphinx 3.0.2.
diff --git a/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html b/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html
index ac0e819..220cd32 100644
--- a/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html
+++ b/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html
@@ -1,19 +1,18 @@
-
+
-
+
-
-
+
Using x to Generate Values — Thun 0.2.0 documentation
-
-
-
-
-
+
+
+
+
+
+
@@ -30,12 +29,14 @@
+
+
Using x to Generate Values¶
Cf. jp-reprod.html
-from notebook_preamble import J, V, define
+from notebook_preamble import J, V, define
Consider the x combinator:
@@ -75,7 +76,7 @@ function C
Let’s try it:
-V('[0 swap [dup ++] dip rest cons] x')
+V('[0 swap [dup ++] dip rest cons] x')
. [0 swap [dup ++] dip rest cons] x
@@ -94,7 +95,7 @@ function C
After one application of x the quoted program contains 1 and
0 is below it on the stack.
-J('[0 swap [dup ++] dip rest cons] x x x x x pop')
+