Skip to content

Commit 3859678

Browse files
authored
Add files via upload
1 parent f9bbb8e commit 3859678

File tree

7 files changed

+81
-55
lines changed

7 files changed

+81
-55
lines changed

bytearraydemo.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
import zlib
1010
from hashlib import sha256
1111

12-
s = "This will be turned into an array of bytes \U0001F603, man!"
12+
s = "Here is an example Unicode string \U0001F603, man!"
1313

14-
print(f"Length of s is {len(s)} characters.")
14+
print(f"Length of original string is {len(s)} characters.")
1515
sb = s.encode('utf-8')
16-
print(f"Length of sb is {len(sb)} bytes.")
16+
print(f"Length of extracted byte array is {len(sb)} bytes.")
1717
s = sb.decode('utf-8')
1818
print(f"Decoding sb gives back the string:\n{repr(s)}.")
1919

@@ -33,7 +33,9 @@
3333
print(f"Its SHA-256 checksum is:\n{chk.hexdigest()}")
3434

3535
# Natural language contains so much redundancy and uses so few of
36-
# the available charactes and words that it compresses massively.
36+
# the available character and word combinations that usually it
37+
# compresses massively without any information loss.
38+
3739
sbc = zlib.compress(sb)
3840
print(f"Compressed, it needs only {len(sbc)} bytes.")
3941

functional.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def rising_power(n, k):
4848

4949
# Determine whether the sequence x, f(x), f(f(f(x))), ... becomes
5050
# periodic after some point. A computer science classic without
51-
# needing more than O(1) extra memory.
51+
# needing more than constant amount of extra memory.
5252

5353

5454
def is_eventually_periodic(f, x, giveup=1000):
@@ -123,6 +123,7 @@ def lookup_f(*args):
123123
res = f(*args) # calculate the result
124124
results[args] = res # and store it in dictionary
125125
return res
126+
126127
# Alias the local variable so it can be seen from outside.
127128
lookup_f.results = results
128129
# Result is a function that can be called same as original.
@@ -151,14 +152,14 @@ def fib(n):
151152
# http://paulbourke.net/fractals/qseries/
152153

153154

155+
@memoize
154156
def hof_q(n):
155157
if n < 3:
156158
return 1
157159
else:
158-
return hof_q(n - hof_q(n-1)) + hof_q(n - hof_q(n-2))
160+
return hof_q(n - hof_q(n - 1)) + hof_q(n - hof_q(n - 2))
159161

160162

161-
hof_q = memoize(hof_q)
162163
print(f"HofQ(100) = {hof_q(100)}.")
163164

164165
# We can also perform the memoization explicitly. Since the function
@@ -190,7 +191,7 @@ def hof_qt(n):
190191
def wine(barrel, age, year, pour=Fraction(1, 2)):
191192
# Imaginary "zero" barrel to represent incoming flow of new wine.
192193
if barrel == 0:
193-
return 1 if age == 0 else 0
194+
return Fraction(1) if age == 0 else 0
194195
# In the initial state, all barrels consist of new wine.
195196
elif year == 0:
196197
return 1 if age == 0 else 0
@@ -268,8 +269,8 @@ def collatz(n):
268269
def thue_morse(n, sign):
269270
global __tm_call_count
270271
__tm_call_count += 1
271-
if n == 1:
272-
return '0' if sign == 0 else '1'
272+
if n < 2:
273+
return f"{str(sign)}"
273274
else:
274275
return thue_morse(n - 1, sign) + thue_morse(n - 1, 1 - sign)
275276

morse.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,25 @@
33
# The dictionary of Morse codes and the corresponding letters.
44

55
codes = {
6-
".-": "a", "-...": "b", "-.-.": "c", "-..": "d",
7-
".": "e", "..-.": "f", "--.": "g", "....": "h",
8-
"..": "i", ".---": "j", "-.-": "k", ".-..": "l",
9-
"--": "m", "-.": "n", "---": "o", ".--.": "p",
10-
"--.-": "q", ".-.": "r", "...": "s", "-": "t",
11-
"..-": "u", "...-": "v", ".--": "w", "-..-": "x",
12-
"-.--": "y", "--..": "z"
6+
'.-': 'a', '-...': 'b', '-.-.': 'c', '-..': 'd',
7+
'.': 'e', '..-.': 'f', '--.': 'g', '....': 'h',
8+
'..': 'i', '.---': 'j', '-.-': 'k', '.-..': 'l',
9+
'--': 'm', '-.': 'n', '---': 'o', '.--.': 'p',
10+
'--.-': 'q', '.-.': 'r', '...': 's', '-': 't',
11+
'..-': 'u', '...-': 'v', '.--': 'w', '-..-': 'x',
12+
'-.--': 'y', '--..': 'z'
1313
}
1414

15-
# Morse code is not what is known as "prefix code" so that
15+
# Morse code is not what is known as 'prefix code' so that
1616
# no encoding of some character would be a prefix of the
1717
# encoding of some other character. This makes the decoding
1818
# ambiguous unless the Morse operator takes a noticeable
1919
# short pause between the individual letters.
2020

2121
# Construct a reverse dictionary from an existing dictionary
22-
# with this handy dictionary comprehension.
22+
# with this handy dictionary comprehension. If multiple keys
23+
# of the original map to the same value, only the last pair
24+
# of value: key is stored in the reverse dictionary.
2325

2426
codes_r = {codes[v]: v for v in codes}
2527

@@ -29,8 +31,8 @@
2931
# individual letters. Unknown characters are simply skipped
3032
# in this encoding.
3133

32-
def encode_morse(text, sep=""):
33-
return sep.join((codes_r.get(c, "") for c in text.lower()))
34+
def encode_morse(text, sep=''):
35+
return sep.join((codes_r.get(c, '') for c in text.lower()))
3436

3537

3638
# A generator function that yields all possible ways to
@@ -40,8 +42,8 @@ def encode_morse(text, sep=""):
4042
# the rest of the message.
4143

4244
def decode_morse(message):
43-
if message == "":
44-
yield ""
45+
if message == '':
46+
yield ''
4547
else:
4648
for i in range(1, min(len(message) + 1, 5)):
4749
prefix = message[:i]
@@ -51,22 +53,22 @@ def decode_morse(message):
5153
yield head + follow
5254

5355

54-
if __name__ == "__main__":
55-
with open('words_sorted.txt', encoding="utf-8") as f:
56+
if __name__ == '__main__':
57+
with open('words_sorted.txt', encoding='utf-8') as f:
5658
wordlist = [word.strip() for word in f if len(word) < 8]
57-
print(f"Read a list of {len(wordlist)} words.")
59+
print(f'Read a list of {len(wordlist)} words.')
5860

5961
# Convert to set for a quick lookup of individual words.
6062
words = set(wordlist)
6163

6264
for text in sample(wordlist, 20):
6365
enc = encode_morse(text)
64-
print(f"The word {text!r} encodes in Morse to {enc!r}.")
65-
print(f"The Morse code message {enc!r} decodes to words:")
66+
print(f'The word {text!r} encodes in Morse to {enc!r}.')
67+
print(f'The Morse code message {enc!r} decodes to words:')
6668
dec = [word for word in decode_morse(enc) if word in words]
6769
for word in dec:
6870
print(f"{word!r} split as {encode_morse(word, ' ')}")
69-
print("")
71+
print('')
7072

71-
# Exercise for the reader: find the word whose encoding in Morse
72-
# can be decoded to largest possible number of different words.
73+
# Exercise for the reader: find the largest group of words that
74+
# all encode to the exact same series of Morse dots and dashes.

recursion.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@
66
# Many problems can be solved surprisingly easily by first
77
# solving a smaller version of that problem, whose result is
88
# then productively used in solving the original problem. The
9-
# classic textbook first example of recursion is factorial.
9+
# classic textbook first example of recursion is factorial
10+
# whose formula n! = 1 * 2 * ... * (n-1) * n is self-similar.
1011

1112

12-
def factorial(n):
13+
@functools.lru_cache()
14+
def factorial(n, verbose=False):
15+
if verbose:
16+
print(f"enter with n = {n}")
1317
if n < 2:
14-
return 1 # base case
18+
result = 1 # base case
1519
else:
16-
return n * factorial(n - 1) # recursive call
20+
result = n * factorial(n - 1, verbose) # linear recursive call
21+
if verbose:
22+
print(f"Returning {result} from {n}")
23+
return result
1724

1825

1926
# Functional programming version, if only for humour value:
@@ -49,11 +56,11 @@ def factorial_it(n):
4956

5057
def hanoi(src, tgt, n):
5158
if n < 1:
52-
return
59+
return None
5360
mid = 6 - src - tgt
54-
hanoi(src, mid, n-1)
61+
hanoi(src, mid, n - 1)
5562
print(f"Move top disk from peg {src} to peg {tgt}.")
56-
hanoi(mid, tgt, n-1)
63+
hanoi(mid, tgt, n - 1)
5764

5865

5966
# For computing high integer powers, binary power is more efficient
@@ -63,27 +70,28 @@ def binary_power(a, n, verbose=False):
6370
if verbose:
6471
print(f"Entering binary_power({n}).")
6572
if n < 0:
66-
raise ValueError("Binary power negative exponent not allowed.")
73+
raise ValueError(f"Negative exponent {n} not allowed.")
6774
elif n == 0:
6875
result = 1
6976
elif n % 2 == 0:
7077
result = binary_power(a * a, n // 2, verbose)
7178
else:
72-
result = a * binary_power(a * a, (n-1) // 2, verbose)
79+
result = a * binary_power(a * a, (n - 1) // 2, verbose)
7380
if verbose:
7481
print(f"Exiting binary_power({n}) with {result}.")
7582
return result
7683

7784

7885
# Flattening a list that can contain other lists as elements is a
7986
# classic list programming exercise. This being Python, the correct
80-
# "duck typing" spirit of checking of something is a list is to
81-
# check if it is iterable, that is, for our purposes it behaves
82-
# like a list.
87+
# "duck typing" spirit of checking of something is an iterable
88+
# sequence is to check if it allows creation of iterator objects
89+
# with the magic method __iter__, that is, for our purposes it acts
90+
# like a sequence.
8391

84-
def flatten(li):
92+
def flatten(items):
8593
result = []
86-
for x in li:
94+
for x in items:
8795
if hasattr(x, '__iter__'): # is x an iterable?
8896
result.extend(flatten(x))
8997
else:
@@ -104,10 +112,12 @@ def subset_sum(items, goal):
104112
if len(items) == 0 or goal < 0:
105113
return None
106114
last = items.pop()
107-
answer = subset_sum(items, goal-last)
115+
# Take the last element into chosen subset
116+
answer = subset_sum(items, goal - last)
108117
if answer is not None:
109118
answer.append(last)
110119
else:
120+
# Try not taking last element into subset
111121
answer = subset_sum(items, goal)
112122
items.append(last)
113123
return answer
@@ -167,7 +177,7 @@ def generate_tour(cx, cy):
167177

168178
def ackermann(m, n):
169179
if m == 0:
170-
return n+1
180+
return n + 1
171181
elif m > 0 and n == 0:
172182
return ackermann(m - 1, 1)
173183
else:

reservoir.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from random import randint
22

33

4-
# Randomly choose k elements from given iterator.
4+
# Randomly choose k elements from given iterable.
55

66
def reservoir(items, k):
77
buffer = []
@@ -13,13 +13,12 @@ def reservoir(items, k):
1313
if idx < k: # The new element hits the reservoir.
1414
buffer[idx] = v # displace some previous element
1515
count += 1
16-
# Having read in every item, emit the reservoir elements.
17-
yield from buffer
16+
yield from buffer # Emit the reservoir elements.
1817

1918

2019
if __name__ == "__main__":
2120
print("Here are 20 random non-short lines from 'War and Peace':")
22-
with open('warandpeace.txt', encoding="utf-8") as wap:
21+
with open('warandpeace.txt', encoding='utf-8') as wap:
2322
source = enumerate(reservoir((x.strip() for x in wap
2423
if len(x) > 60), 20))
2524
for (idx, line) in source:

wordfill.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
# previous i - 1 horizontal and vertical words. If babbage is
99
# set to True, the word square must have same rows and columns.
1010

11+
# For example, the incomplete square
12+
#
13+
# hello
14+
# oasis
15+
# tt
16+
# ee
17+
# ln
18+
#
19+
# would be represented by parameter values n = 5, i = 2, vv = 0,
20+
# horiz = ['hello', 'oasis'] and vert = ['hotel', 'eaten'].
21+
1122
def wordfill(n, i, horiz, vert, wordlist, vv, babbage=False):
1223
# Entire square is complete when rows 0, ..., n-1 are filled.
1324
if i == n:
@@ -23,7 +34,7 @@ def wordfill(n, i, horiz, vert, wordlist, vv, babbage=False):
2334
# Try using that word as the horizontal word.
2435
if not (word in horiz or word in vert):
2536
horiz.append(word)
26-
if babbage:
37+
if babbage: # Horizontal and vertical words equal
2738
w = wordfill(n, i+1, horiz, horiz, wordlist, 0, True)
2839
else:
2940
w = wordfill(n, i+vv, vert, horiz, wordlist, 1-vv)
@@ -53,7 +64,7 @@ def wordfill(n, i, horiz, vert, wordlist, vv, babbage=False):
5364
i1 = bisect.bisect_left(wordlist, w1[0])
5465
i2 = bisect.bisect_right(wordlist, chr(ord(w1[0]) + 1))
5566
# Choose one of those words as the first vertical word.
56-
w2 = wordlist[random.randint(i1, i2-1)]
67+
w2 = wordlist[random.randint(i1, i2 - 1)]
5768
print(f"Trying out starting words {w1}, {w2}...", end=" ")
5869
found = False
5970
for sol in it.islice(wordfill(n, 1, [w1], [w2], wordlist, 0), 1):

wordproblems.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def longest_substring_with_k_chars(text, k=2):
137137
# Extract the longest found substring as the answer.
138138
return text[maxpos:maxpos + max_]
139139

140+
140141
# Given a sorted list of words and the first word, construct a
141142
# word chain in which each word starts with the suffix of the
142143
# previous word with the first k characters removed, for example
@@ -201,8 +202,8 @@ def remain_words(words):
201202
def all_anagrams(words):
202203
codes = {}
203204
# The first 26 prime numbers, one for each letter from a to z.
204-
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
205-
53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
205+
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
206+
47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
206207
for word in words:
207208
m = 1
208209
for c in word:

0 commit comments

Comments
 (0)