diff --git a/advanced-iterators.html b/advanced-iterators.html index 5cb4f44..25e3ee7 100644 --- a/advanced-iterators.html +++ b/advanced-iterators.html @@ -17,11 +17,289 @@ body{counter-reset:h1 6}

Diving In

FIXME +

[download alphametics.py] +

import re
+import itertools
+
+def solve(puzzle):
+    words = re.findall('[A-Z]+', puzzle.upper())
+    unique_characters = {c for c in ''.join(words)}
+    assert len(unique_characters) <= 10
+    first_letters = {word[0] for word in words}
+    n = len(first_letters)
+    sorted_characters = ''.join(first_letters) + \
+        ''.join(unique_characters - first_letters)
+    characters = tuple(ord(c) for c in sorted_characters)
+    digits = tuple(ord(c) for c in '0123456789')
+    zero = digits[0]
+    for guess in itertools.permutations(digits, len(characters)):
+        if zero not in guess[:n]:
+            equation = puzzle.translate(dict(zip(characters, guess)))
+            if eval(equation):
+                return equation
+
+if __name__ == '__main__':
+    import sys
+    for puzzle in sys.argv[1:]:
+        print(puzzle)
+        solution = solve(puzzle)
+        if solution:
+            print(solution)
+ +
+you@localhost:~$ python3 alphametics.py "SEND + MORE == MONEY"
+SEND + MORE == MONEY
+9567 + 1085 == 10652
+you@localhost:~$ python3 alphametics.py "I + LOVE + YOU == DORA"
+I + LOVE + YOU == DORA
+1 + 2784 + 975 == 3760
+ +

Finding all occurrences of a pattern

+ +

FIXME + +

+>>> import re
+>>> re.findall('[A-Z]+', 'SEND + MORE == MONEY')
+['SEND', 'MORE', 'MONEY']
+ +

FIXME + +

Finding the unique items in a sequence

+ +
+>>> a_list = ['a', 'c', 'b', 'a', 'd', 'b']
+>>> {c for c in a_list}
+{'a', 'c', 'b', 'd'}
+>>> a_string = 'EAST IS EAST'
+>>> {c for c in a_string}
+{'A', ' ', 'E', 'I', 'S', 'T'}
+>>> words = ['SEND', 'MORE', 'MONEY']
+>>> ''.join(words)
+'SENDMOREMONEY'
+>>> {c for c in ''.join(words)}
+{'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}
+ +

FIXME + +

Making assertions

+ +

FIXME + +

+>>> assert 1 + 1 = 2
+>>> assert 1 + 1 = 3
+Traceback (most recent call last):
+  File "<stdin>", line 1, in 
+AssertionError
+ +

FIXME + +

assert len(unique_characters) <= 10
+ +

…is equivalent to… + +

if len(unique_characters) > 10:
+    raise AssertionError
+ +

Generator objects

+ +

FIXME + +

+>>> unique_characters = {'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}
+>>> gen = (ord(c) for c in unique_characters)
+>>> gen
+<generator object <genexpr> at 0x00BADC10>
+>>> next(gen)
+69
+>>> next(gen)
+68
+>>> tuple(ord(c) for c in unique_characters)
+(69, 68, 77, 79, 78, 83, 82, 89)
+ +

FIXME + +

Calculating Permutations… The Lazy Way!

+ +

FIXME what are permutations? + +

+>>> import itertools
+>>> perms = itertools.permutations([1, 2, 3], 2)
+>>> next(perms)
+(1, 2)
+>>> next(perms)
+(1, 3)
+>>> next(perms)
+(2, 1)
+>>> next(perms)
+(2, 3)
+>>> next(perms)
+(3, 1)
+>>> next(perms)
+(3, 2)
+>>> next(perms)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in 
+StopIteration
+ +

FIXME + +

+>>> import itertools
+>>> perms = itertools.permutations('ABC', 3)
+>>> next(perms)
+('A', 'B', 'C')
+>>> next(perms)
+('A', 'C', 'B')
+>>> next(perms)
+('B', 'A', 'C')
+>>> next(perms)
+('B', 'C', 'A')
+>>> next(perms)
+('C', 'A', 'B')
+>>> next(perms)
+('C', 'B', 'A')
+>>> next(perms)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in 
+StopIteration
+>>> list(itertools.permutations('ABC', 3))
+[('A', 'B', 'C'), ('A', 'C', 'B'),
+ ('B', 'A', 'C'), ('B', 'C', 'A'),
+ ('C', 'A', 'B'), ('C', 'B', 'A')]
+ +

FIXME + +

Other Fun Stuff In The itertools Module

+
+>>> import itertools
+>>> list(itertools.product('ABC', '123'))
+[('A', '1'), ('A', '2'), ('A', '3'), 
+ ('B', '1'), ('B', '2'), ('B', '3'), 
+ ('C', '1'), ('C', '2'), ('C', '3')]
+>>> list(itertools.combinations('ABC', 2))
+[('A', 'B'), ('A', 'C'), ('B', 'C')]
+ +

FIXME + +

+>>> names = list(open('examples/favorite-people.txt'))
+>>> names
+['Dora\n', 'Ethan\n', 'Wesley\n', 'John\n', 'Anne\n',
+'Mike\n', 'Chris\n', 'Sarah\n', 'Alex\n', 'Lizzie\n']
+>>> names = [name[:-1] for name in names]
+>>> names
+['Dora', 'Ethan', 'Wesley', 'John', 'Anne',
+'Mike', 'Chris', 'Sarah', 'Alex', 'Lizzie']
+>>> names = sorted(names)
+>>> names
+['Alex', 'Anne', 'Chris', 'Dora', 'Ethan',
+'John', 'Lizzie', 'Mike', 'Sarah', 'Wesley']
+>>> names = sorted(names, key=len)
+>>> names
+['Alex', 'Anne', 'Dora', 'John', 'Mike',
+'Chris', 'Ethan', 'Sarah', 'Lizzie', 'Wesley']
+>>> import itertools
+>>> groups = itertools.groupby(names, len)
+>>> groups
+<itertools.groupby object at 0x00BB20C0>
+>>> list(groups)
+[(4, <itertools._grouper object at 0x00BA8BF0>),
+ (5, <itertools._grouper object at 0x00BB4050>),
+ (6, <itertools._grouper object at 0x00BB4030>)]
+>>> groups = itertools.groupby(names, len)
+>>> for name_length, name_iter in groups:
+...     print('Names with {0:d} letters:'.format(name_length))
+...     for name in name_iter:
+...         print(name)
+... 
+Names with 4 letters:
+Alex
+Anne
+Dora
+John
+Mike
+Names with 5 letters:
+Chris
+Ethan
+Sarah
+Names with 6 letters:
+Lizzie
+Wesley
+ +

FIXME + +

Combining Iterators

+ +

FIXME + +

+>>> list(range(0, 3))
+[0, 1, 2]
+>>> list(range(10, 13))
+[10, 11, 12]
+>>> list(itertools.chain(range(0, 3), range(10, 13)))
+[0, 1, 2, 10, 11, 12]
+>>> list(zip(range(0, 3), range(10, 13)))
+[(0, 10), (1, 11), (2, 12)]
+>>> list(zip(range(0, 3), range(10, 14)))
+[(0, 10), (1, 11), (2, 12)]
+>>> list(itertools.zip_longest(range(0, 3), range(10, 14)))
+[(0, 10), (1, 11), (2, 12), (None, 13)]
+ +

FIXME + +

+>>> characters = ('S', 'M', 'E', 'D', 'O', 'N', 'R', 'Y')
+>>> guess = ('1', '2', '0', '3', '4', '5', '6', '7')
+>>> tuple(zip(characters, guess))
+(('S', '1'), ('M', '2'), ('E', '0'), ('D', '3'),
+ ('O', '4'), ('N', '5'), ('R', '6'), ('Y', '7'))
+>>> dict(zip(characters, guess))
+{'E': '0', 'D': '3', 'M': '2', 'O': '4',
+ 'N': '5', 'S': '1', 'R': '6', 'Y': '7'}
+ +

A New Kind Of String Manipulation

+ +
+>>> characters = tuple(ord(c) for c in 'SMEDONRY')
+>>> characters
+(83, 77, 69, 68, 79, 78, 82, 89)
+>>> digits = tuple(ord(c) for c in '0123456789')
+>>> digits
+(48, 49, 50, 51, 52, 53, 54, 55, 56, 57)
+>>> guess = (49, 50, 48, 51, 52, 53, 54, 55)
+>>> translation_table = dict(zip(characters, guess))
+>>> translation_table
+{68: 51, 69: 48, 77: 50, 78: 53, 79: 52, 82: 54, 83: 49, 89: 55}
+>>> "SEND + MORE == MONEY".translate(translation_table)
+'1053 + 2460 == 24507'
+ +

FIXME + +

+>>> translation_table = {ord("A"): ord("O")}
+>>> translation_table
+{65: 79}
+>>> 'MARK'.translate(translation_table)
+'MORK'
+ +

FIXME + +

Evaluating Arbitrary Strings As Python Expressions

+ +

FIXME + +

Putting It All Together

+ +

FIXME +

Further Reading

- + +

FIXME

© 2001–9 ark Pilgrim - diff --git a/examples/alphametics.py b/examples/alphametics.py index dd64677..2476eaa 100644 --- a/examples/alphametics.py +++ b/examples/alphametics.py @@ -8,18 +8,20 @@ import re import itertools def solve(puzzle): - words = re.findall('[A-Z]+', puzzle) - unique_characters = set(''.join(words)) + words = re.findall('[A-Z]+', puzzle.upper()) + unique_characters = {c for c in ''.join(words)} assert len(unique_characters) <= 10 - first_letters = set(word[0] for word in words) + first_letters = {word[0] for word in words} n = len(first_letters) sorted_characters = ''.join(first_letters) + \ ''.join(unique_characters - first_letters) - characters = tuple(map(ord, sorted_characters)) - digits = tuple(map(ord, '0123456789')) + characters = tuple(ord(c) for c in sorted_characters) + digits = tuple(ord(c) for c in '0123456789') zero = digits[0] for guess in itertools.permutations(digits, len(characters)): if zero not in guess[:n]: + print(guess) + return equation = puzzle.translate(dict(zip(characters, guess))) if eval(equation): return equation