# Copyright 2014 Brett Slatkin, Pearson Education Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Preamble to mimick book environment
import logging
from pprint import pprint
from sys import stdout as STDOUT
# Example 1
def my_coroutine():
while True:
received = yield
print('Received:', received)
it = my_coroutine()
next(it) # Prime the coroutine
it.send('First')
it.send('Second')
Received: First Received: Second
# Example 2
def minimize():
current = yield
while True:
value = yield current
current = min(value, current)
# Example 3
it = minimize()
next(it) # Prime the generator
print(it.send(10))
print(it.send(4))
print(it.send(22))
print(it.send(-1))
10 4 4 -1
# Example 4
ALIVE = '*'
EMPTY = '-'
# Example 5
from collections import namedtuple
Query = namedtuple('Query', ('y', 'x'))
# Example 6
def count_neighbors(y, x):
n_ = yield Query(y + 1, x + 0) # North
ne = yield Query(y + 1, x + 1) # Northeast
# Define e_, se, s_, sw, w_, nw ...
e_ = yield Query(y + 0, x + 1) # East
se = yield Query(y - 1, x + 1) # Southeast
s_ = yield Query(y - 1, x + 0) # South
sw = yield Query(y - 1, x - 1) # Southwest
w_ = yield Query(y + 0, x - 1) # West
nw = yield Query(y + 1, x - 1) # Northwest
neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw]
count = 0
for state in neighbor_states:
if state == ALIVE:
count += 1
return count
# Example 7
it = count_neighbors(10, 5)
q1 = next(it) # Get the first query
print('First yield: ', q1)
q2 = it.send(ALIVE) # Send q1 state, get q2
print('Second yield:', q2)
q3 = it.send(ALIVE) # Send q2 state, get q3
print('...')
q4 = it.send(EMPTY)
q5 = it.send(EMPTY)
q6 = it.send(EMPTY)
q7 = it.send(EMPTY)
q8 = it.send(EMPTY)
try:
it.send(EMPTY) # Send q8 state, retrieve count
except StopIteration as e:
print('Count: ', e.value) # Value from return statement
First yield: Query(y=11, x=5) Second yield: Query(y=11, x=6) ... Count: 2
# Example 8
Transition = namedtuple('Transition', ('y', 'x', 'state'))
# Example 9
def game_logic(state, neighbors):
pass
def step_cell(y, x):
state = yield Query(y, x)
neighbors = yield from count_neighbors(y, x)
next_state = game_logic(state, neighbors)
yield Transition(y, x, next_state)
# Example 10
def game_logic(state, neighbors):
if state == ALIVE:
if neighbors < 2:
return EMPTY # Die: Too few
elif neighbors > 3:
return EMPTY # Die: Too many
else:
if neighbors == 3:
return ALIVE # Regenerate
return state
# Example 11
it = step_cell(10, 5)
q0 = next(it) # Initial location query
print('Me: ', q0)
q1 = it.send(ALIVE) # Send my status, get neighbor query
print('Q1: ', q1)
print('...')
q2 = it.send(ALIVE)
q3 = it.send(ALIVE)
q4 = it.send(ALIVE)
q5 = it.send(ALIVE)
q6 = it.send(EMPTY)
q7 = it.send(EMPTY)
q8 = it.send(EMPTY)
t1 = it.send(EMPTY) # Send for q8, get game decision
print('Outcome: ', t1)
Me: Query(y=10, x=5) Q1: Query(y=11, x=5) ... Outcome: Transition(y=10, x=5, state='-')
# Example 12
TICK = object()
def simulate(height, width):
while True:
for y in range(height):
for x in range(width):
yield from step_cell(y, x)
yield TICK
# Example 13
class Grid(object):
def __init__(self, height, width):
self.height = height
self.width = width
self.rows = []
for _ in range(self.height):
self.rows.append([EMPTY] * self.width)
def __str__(self):
output = ''
for row in self.rows:
for cell in row:
output += cell
output += '\n'
return output
# Example 14
def query(self, y, x):
return self.rows[y % self.height][x % self.width]
def assign(self, y, x, state):
self.rows[y % self.height][x % self.width] = state
# Example 15
def live_a_generation(grid, sim):
progeny = Grid(grid.height, grid.width)
item = next(sim)
while item is not TICK:
if isinstance(item, Query):
state = grid.query(item.y, item.x)
item = sim.send(state)
else: # Must be a Transition
progeny.assign(item.y, item.x, item.state)
item = next(sim)
return progeny
# Example 16
grid = Grid(5, 9)
grid.assign(0, 3, ALIVE)
grid.assign(1, 4, ALIVE)
grid.assign(2, 2, ALIVE)
grid.assign(2, 3, ALIVE)
grid.assign(2, 4, ALIVE)
print(grid)
---*----- ----*---- --***---- --------- ---------
# Example 17
class ColumnPrinter(object):
def __init__(self):
self.columns = []
def append(self, data):
self.columns.append(data)
def __str__(self):
row_count = 1
for data in self.columns:
row_count = max(row_count, len(data.splitlines()) + 1)
rows = [''] * row_count
for j in range(row_count):
for i, data in enumerate(self.columns):
line = data.splitlines()[max(0, j - 1)]
if j == 0:
padding = ' ' * (len(line) // 2)
rows[j] += padding + str(i) + padding
else:
rows[j] += line
if (i + 1) < len(self.columns):
rows[j] += ' | '
return '\n'.join(rows)
columns = ColumnPrinter()
sim = simulate(grid.height, grid.width)
for i in range(5):
columns.append(str(grid))
grid = live_a_generation(grid, sim)
print(columns)
0 | 1 | 2 | 3 | 4 ---*----- | --------- | --------- | --------- | --------- ----*---- | --*-*---- | ----*---- | ---*----- | ----*---- --***---- | ---**---- | --*-*---- | ----**--- | -----*--- --------- | ---*----- | ---**---- | ---**---- | ---***--- --------- | --------- | --------- | --------- | ---------
# Example 18
def delegated():
yield 1
yield 2
def composed():
yield 'A'
for value in delegated(): # yield from in Python 3
yield value
yield 'B'
print(list(composed()))
['A', 1, 2, 'B']
# Example 19
class MyReturn(Exception):
def __init__(self, value):
self.value = value
def delegated():
yield 1
raise MyReturn(2) # return 2 in Python 3
yield 'Not reached'
def composed():
try:
for value in delegated():
yield value
except MyReturn as e:
output = e.value
yield output * 4
print(list(composed()))
[1, 8]
# Example 20
# This is for the introductory diagram
grid = Grid(5, 5)
grid.assign(1, 1, ALIVE)
grid.assign(2, 2, ALIVE)
grid.assign(2, 3, ALIVE)
grid.assign(3, 3, ALIVE)
columns = ColumnPrinter()
sim = simulate(grid.height, grid.width)
for i in range(5):
columns.append(str(grid))
grid = live_a_generation(grid, sim)
print(columns)
0 | 1 | 2 | 3 | 4 ----- | ----- | ----- | ----- | ----- -*--- | --*-- | --**- | --*-- | ----- --**- | --**- | -*--- | -*--- | -**-- ---*- | --**- | --**- | --*-- | ----- ----- | ----- | ----- | ----- | -----