Working with Collections
We achieve better reuse by separating this simple pairing function. This, in the
long run, is one of our goals. If we build up a library of helpful primitives such
as this pairing function, we can tackle problems more quickly and confidently.
There are many ways to pair up the points along the route to create start and
stop information for each leg. We'll look at a few here and then revisit this in
Chapter 5, Higher-order Functions and again in Chapter 8, The Itertools Module.
Creating pairs can be done in a purely functional way using a recursion.
The following is one version of a function to pair up the points along a route:
def pairs(iterable):
def pair_from( head, iterable_tail ):
nxt= next(iterable_tail)
yield head, nxt
yield from pair_from( nxt, iterable_tail )
try:
return pair_from( next(iterable), iterable )
except StopIteration:
return
The essential function is the internal pair_from() function. This works with the
item at the head of an iterable plus the iterable itself. It yields the first pair, pops
the next item from the iterable, and then invokes itself recursively to yield any
additional pairs.
We've invoked this function from the pairs() function. The pairs() function
ensures that the initialization is handled properly and the terminating exception
is silenced properly.
Python iterable recursion involves a for loop to properly consume
and yield the results from the recursion. If we try to use a simpler-
looking return pair_from(nxt, iterable_tail) method,
we'll see that it does not properly consume the iterable and yield
all of the values.
Recursion in a generator function requires yield from a statement
to consume the resulting iterable. For this, use yield from
recursive_iter(args).
Something like return recursive_iter(args) will return
only a generator object; it doesn't evaluate the function to return
the generated values.