Skip to content

Commit 624ceb0

Browse files
authored
Add files via upload
1 parent 16af6a5 commit 624ceb0

File tree

5 files changed

+175
-125
lines changed

5 files changed

+175
-125
lines changed

bytearraydemo.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,45 @@
1-
# Strings contain unicode characters somehow internally stored. They
2-
# can be extracted to a byte array with given Unicode encoding. This
3-
# byte array can be processed with byte array operations such as
4-
# checksums and compressions, not caring where these bytes came from.
1+
# Strings a sequences of unicode characters, and the encoding
2+
# used internally to store a string object does not show on the
3+
# outside. The contents of each string can be extracted into a
4+
# byte array in desired Unicode encoding. This byte array can
5+
# be processed further with operations such as checksums and
6+
# compressions, without a care where these bytes originally
7+
# came from and what they were considered to represent there.
58

69
import zlib
710
from hashlib import sha256
811

9-
s = "This will be turned into an array of bytes \U0001F603."
12+
s = "This will be turned into an array of bytes \U0001F603, man!"
1013

11-
print(f"Length of s is {len(s)}.")
14+
print(f"Length of s is {len(s)} characters.")
1215
sb = s.encode('utf-8')
13-
print(f"Length of bytes is {len(sb)}.")
16+
print(f"Length of sb is {len(sb)} bytes.")
1417
s = sb.decode('utf-8')
15-
print(f"Decoding bytes gives the string {repr(s)}.")
18+
print(f"Decoding sb gives back the string:\n{repr(s)}.")
1619

17-
print(f"Adler checksum of string s: {zlib.adler32(sb)}")
18-
print(f"CRC32 checksum of string s: {zlib.crc32(sb)}")
20+
print(f"Adler checksum of string is {zlib.adler32(sb)}.")
21+
print(f"CRC32 checksum of string is {zlib.crc32(sb)}.")
1922

2023
with open('warandpeace.txt', encoding="utf-8") as wap:
21-
s = " ".join(list(wap)) # this will be a really long string
24+
s = " ".join(list(wap)) # This will be one long string.
25+
2226
print(f"'War and Peace' contains {len(s)} characters.")
2327

2428
sb = s.encode('utf-8')
2529
print(f"Encoded as byte array using utf-8, this gives {len(sb)} bytes.")
2630

27-
chk = sha256() # Cryptographic checksum, a bit slower to compute
31+
chk = sha256() # Cryptographic checksum, a bit slower to compute.
2832
chk.update(sb)
29-
print(f"SHA 256 checksum is: <{chk.hexdigest()}>")
33+
print(f"Its SHA-256 checksum is:\n{chk.hexdigest()}")
3034

35+
# Natural language contains so much redundancy and uses so few of
36+
# the available charactes and words that it compresses massively.
3137
sbc = zlib.compress(sb)
32-
print(f"Compressed, it has only {len(sbc)} bytes.")
38+
print(f"Compressed, it needs only {len(sbc)} bytes.")
3339

3440
sd = zlib.decompress(sbc)
35-
assert sd == sb # Decompression restores the original data.
41+
assert sd == sb # Decompression brings back the original data.
3642

3743
sbd = sb.decode('utf-8')
38-
print(f"Decompressed 'War and Peace' again has {len(sbd)} characters.")
39-
assert s == sbd
44+
print(f"Decompressed 'War and Peace' has {len(sbd)} characters.")
45+
assert s == sbd # The original data produces an equal string.

functional.py

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
from fractions import Fraction
44
from random import randint
55

6-
# This example demonstrates a bunch of Python features for functional
7-
# programming. This means that functions are treated as data that can
8-
# be stored to variables, passed to other functions, returned from
9-
# functions as results, created on the fly etc.
6+
# This example demonstrates some of Python features for functional
7+
# programming. This means that functions are treated as data that
8+
# can be stored to variables, passed to other functions, returned
9+
# from functions as results, created on the fly as needed, etc.
1010

1111

1212
def is_positive(n):
@@ -21,13 +21,15 @@ def square(n):
2121
# Note that these both return iterator objects instead of doing
2222
# the computation right away, so we force the computation to
2323
# take place with list().
24+
2425
print("The positive elements are: ", end='')
2526
print(list(filter(is_positive, [-7, 8, 42, -1, 0])))
27+
2628
print("The squares of first five positive integers are: ", end='')
2729
print(list(map(square, [1, 2, 3, 4, 5])))
2830

29-
# Reduce is a handy operation to repeatedly combine first two elements
30-
# of the given sequence into one, until only one element remains.
31+
# Reduce is a handy operation to repeatedly combine first two
32+
# elements of given sequence into one, until only one remains.
3133

3234
print(reduce(mul, [1, 2, 3, 4, 5], 1)) # 120 = 5!
3335

@@ -57,20 +59,24 @@ def is_eventually_periodic(f, x, giveup=1000):
5759
giveup -= 1
5860
return tortoise == hare
5961

60-
# Next, let's examine how functions can be given to other functions as
61-
# parameters, and returned as results to the caller. To demonstrate this,
62-
# let's write a function negate that can be given any predicate function,
63-
# and it creates and returns a function that behaves as the negation of
62+
# Next, let's examine how functions can be given to other functions
63+
# as parameters, and returned as results to the caller. To demo this,
64+
# let's write a function negate that can be given any predicate, and
65+
# it creates and returns a function that behaves as the negation of
6466
# its parameter.
6567

6668

6769
def negate(f):
6870
# Nothing says that we can't define a function inside a function.
69-
# Since we don't know what parameters f takes, we write our function
70-
# to accept any arguments and keyword arguments.
71-
def result(*args, **kwargs):
71+
# Since we don't know what parameters f takes, we write this
72+
# function to accept any arguments and keyword arguments that
73+
# it simply passes down to the original f without further ado.
74+
75+
def negated_f(*args, **kwargs):
7276
return not f(*args, **kwargs)
73-
return result # return the function that we just defined
77+
78+
# Return the function that was just defined.
79+
return negated_f
7480

7581
# Let's try this out by negating the following simple function.
7682

@@ -79,30 +85,32 @@ def is_odd(x):
7985
return x % 2 != 0
8086

8187

82-
is_even = negate(is_odd)
83-
print(is_even) # <function result at ....>
88+
is_even = negate(is_odd) # I can't even...
89+
print(is_even) # <function result at ....>
8490
print(f"Is 2 even? {is_even(2)}") # True
8591
print(f"Is 3 even? {is_even(3)}") # False
8692

87-
# Partial evaluation or "currying" means fixing some of the parameter
88-
# values of a function to create a function that takes fewer parameters
89-
# but is otherwise the same.
9093

94+
# Partial evaluation or "currying" means setting some of the
95+
# arguments of a function to stone, to create a new function
96+
# that takes fewer parameters but works otherwise the same as
97+
# the original function.
9198

9299
def foo(a, b, c):
93100
return a + (b * c)
94101

95102

96103
# Create a version of foo with parameter b fixed to -1.
104+
97105
foob = partial(foo, b=-1)
98-
print(f"After partial application, foob(2, 5)={foob(a = 2, c = 5)}.")
106+
print(f"After partial, foob(2, 5)={foob(a=2, c=5)}.")
99107

100108
# Sometimes functions can take a long time to calculate, but we
101109
# know that they will always return the same result for the same
102-
# parameters. The function memoize takes an arbitrary function as
103-
# a parameter, and creates and returns a new function that gives
104-
# the same results as the original, but remembers the results that
105-
# it has already computed and simply looks up those results that
110+
# arguments. The function memoize takes an arbitrary function as
111+
# an argument, and creates and returns a new function that gives
112+
# the same results as the original. However, it remembers what
113+
# it has already computed, and simply looks up those results that
106114
# have previously been calculated.
107115

108116

@@ -199,9 +207,10 @@ def wine(barrel, age, year, pour=Fraction(1, 2)):
199207
print(f"Barrel {b}: {', '.join(comp)}.")
200208

201209

202-
# Wouldn't it be "groovy" to memoize the memoize function itself, so that
203-
# if some function has already been memoized, the same function won't be
204-
# memoized again but its previously memoized version is returned?
210+
# Wouldn't it be "groovy" to memoize the memoize function itself, so
211+
# that if some function has already been memoized, the same function
212+
# won't be redundantly memoized again, but its previously memoized
213+
# version is returned? Isn't duck typing just dandy?
205214

206215
memoize = memoize(memoize)
207216

@@ -214,10 +223,11 @@ def divisible_by_3(x):
214223
f2 = memoize(divisible_by_3) # already did that one
215224
print(f"f1 is f2 = {f1 is f2}.") # True
216225

217-
# We can use the memoization technique to speed up checking whether the
218-
# so-called Collatz sequence starting from the given number eventually
219-
# reaches 1. The function collatz(n) tells how many steps this takes.
220226

227+
# We can use the memoization technique to speed up checking whether
228+
# the so-called Collatz sequence starting from the given number
229+
# eventually reaches 1. The function collatz(n) tells how many
230+
# steps this needs.
221231

222232
@memoize
223233
def collatz(n):
@@ -233,7 +243,7 @@ def collatz(n):
233243
print(f"Collatz sequence from {lc[1]} contains {lc[0]} steps.")
234244
print(f"Stored {len(collatz.results)} results. Some of them are:")
235245

236-
for i in range(10):
246+
for _ in range(10):
237247
v = randint(1, 10**6 - 1)
238248
print(f"From {v}, sequence contains {collatz.results[(v,)]} steps.")
239249

@@ -261,7 +271,7 @@ def thue_morse(n, sign):
261271
if n == 1:
262272
return '0' if sign == 0 else '1'
263273
else:
264-
return thue_morse(n-1, sign) + thue_morse(n-1, 1-sign)
274+
return thue_morse(n - 1, sign) + thue_morse(n - 1, 1 - sign)
265275

266276

267277
print("Thue-Morse sequences from 2 to 10 are:")
@@ -271,21 +281,22 @@ def thue_morse(n, sign):
271281
# Memoization of recursions that have repeating subproblems is awesome.
272282
print(f"Computing those required {__tm_call_count} recursive calls.")
273283

284+
274285
# The next function can be given any number of functions as parameters,
275286
# and it creates and returns a function whose value is the maximum of
276287
# the results of any of these functions.
277288

278-
279289
def max_func(*args):
280290
funcs = args
281291

282-
def result(*args):
292+
def our_max_f(*args):
283293
return max((f(*args) for f in funcs))
284-
return result
294+
295+
return our_max_f
285296

286297

287-
# Try that one out with some polynomials that we create as a lambda
288-
# to be used as throwaway arguments of max_func.
298+
# Try that one out with some polynomials created as lambdas, to be
299+
# used as throwaway arguments of max_func.
289300

290301
f = max_func(lambda x: -(x*x) + 3*x - 7, # -x^2+3x-7
291302
lambda x: 4*x*x - 10*x + 10, # 4x^2-10x+10
@@ -318,14 +329,15 @@ def get_count():
318329
def reset_count():
319330
count[0] = 0
320331

332+
# This is ugly, but necessary. As so many other things in life.
321333
cf.get_count = get_count
322334
cf.reset_count = reset_count
323335
return cf
324336

337+
325338
# Demonstrate the previous decorator by counting how many times the
326339
# sorting algorithm computes the given key...
327340

328-
329341
def kf(x):
330342
return x
331343

@@ -341,7 +353,7 @@ def kf(x):
341353
sorted(range(-100000, 100000), key=kf, reverse=True)
342354
print(f"The key was computed {kf.get_count()} times.")
343355

344-
# Another sometimes handy feature of Python is the ability to compile
356+
# Another sometimes handy feature of Python is its ability to compile
345357
# and execute new code dynamically on the fly, in any desired context.
346358
# However, be careful using eval and exec to strings that originate
347359
# from outside your program, as this opens a doorway for a malicious

0 commit comments

Comments
 (0)