mirror of https://github.com/python/cpython
Fixup this HOWTO's doctest blocks so that they can be run with sphinx' doctest builder.
This commit is contained in:
parent
b98fe5a87f
commit
09a7fe6933
|
@ -2,7 +2,7 @@
|
|||
Functional Programming HOWTO
|
||||
********************************
|
||||
|
||||
:Author: \A. M. Kuchling
|
||||
:Author: A. M. Kuchling
|
||||
:Release: 0.31
|
||||
|
||||
(This is a first draft. Please send comments/error reports/suggestions to
|
||||
|
@ -98,6 +98,7 @@ to the functional style:
|
|||
* Composability.
|
||||
* Ease of debugging and testing.
|
||||
|
||||
|
||||
Formal provability
|
||||
------------------
|
||||
|
||||
|
@ -133,6 +134,7 @@ down or generated a proof, there would then be the question of verifying the
|
|||
proof; maybe there's an error in it, and you wrongly believe you've proved the
|
||||
program correct.
|
||||
|
||||
|
||||
Modularity
|
||||
----------
|
||||
|
||||
|
@ -159,7 +161,6 @@ running a test; instead you only have to synthesize the right input and then
|
|||
check that the output matches expectations.
|
||||
|
||||
|
||||
|
||||
Composability
|
||||
-------------
|
||||
|
||||
|
@ -175,7 +176,6 @@ new programs by arranging existing functions in a new configuration and writing
|
|||
a few functions specialized for the current task.
|
||||
|
||||
|
||||
|
||||
Iterators
|
||||
=========
|
||||
|
||||
|
@ -197,12 +197,12 @@ built-in data types support iteration, the most common being lists and
|
|||
dictionaries. An object is called an **iterable** object if you can get an
|
||||
iterator for it.
|
||||
|
||||
You can experiment with the iteration interface manually::
|
||||
You can experiment with the iteration interface manually:
|
||||
|
||||
>>> L = [1,2,3]
|
||||
>>> it = iter(L)
|
||||
>>> print it
|
||||
<iterator object at 0x8116870>
|
||||
<...iterator object at ...>
|
||||
>>> it.next()
|
||||
1
|
||||
>>> it.next()
|
||||
|
@ -227,7 +227,7 @@ These two statements are equivalent::
|
|||
print i
|
||||
|
||||
Iterators can be materialized as lists or tuples by using the :func:`list` or
|
||||
:func:`tuple` constructor functions::
|
||||
:func:`tuple` constructor functions:
|
||||
|
||||
>>> L = [1,2,3]
|
||||
>>> iterator = iter(L)
|
||||
|
@ -236,7 +236,7 @@ Iterators can be materialized as lists or tuples by using the :func:`list` or
|
|||
(1, 2, 3)
|
||||
|
||||
Sequence unpacking also supports iterators: if you know an iterator will return
|
||||
N elements, you can unpack them into an N-tuple::
|
||||
N elements, you can unpack them into an N-tuple:
|
||||
|
||||
>>> L = [1,2,3]
|
||||
>>> iterator = iter(L)
|
||||
|
@ -269,7 +269,7 @@ sequence type, such as strings, will automatically support creation of an
|
|||
iterator.
|
||||
|
||||
Calling :func:`iter` on a dictionary returns an iterator that will loop over the
|
||||
dictionary's keys::
|
||||
dictionary's keys:
|
||||
|
||||
>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
|
||||
... 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
|
||||
|
@ -279,11 +279,11 @@ dictionary's keys::
|
|||
Feb 2
|
||||
Aug 8
|
||||
Sep 9
|
||||
May 5
|
||||
Apr 4
|
||||
Jun 6
|
||||
Jul 7
|
||||
Jan 1
|
||||
Apr 4
|
||||
May 5
|
||||
Nov 11
|
||||
Dec 12
|
||||
Oct 10
|
||||
|
@ -297,7 +297,7 @@ values, or key/value pairs, you can explicitly call the ``iterkeys()``,
|
|||
``itervalues()``, or ``iteritems()`` methods to get an appropriate iterator.
|
||||
|
||||
The :func:`dict` constructor can accept an iterator that returns a finite stream
|
||||
of ``(key, value)`` tuples::
|
||||
of ``(key, value)`` tuples:
|
||||
|
||||
>>> L = [('Italy', 'Rome'), ('France', 'Paris'), ('US', 'Washington DC')]
|
||||
>>> dict(iter(L))
|
||||
|
@ -406,10 +406,13 @@ equivalent to the following Python code::
|
|||
This means that when there are multiple ``for...in`` clauses but no ``if``
|
||||
clauses, the length of the resulting output will be equal to the product of the
|
||||
lengths of all the sequences. If you have two lists of length 3, the output
|
||||
list is 9 elements long::
|
||||
list is 9 elements long:
|
||||
|
||||
seq1 = 'abc'
|
||||
seq2 = (1,2,3)
|
||||
.. doctest::
|
||||
:options: +NORMALIZE_WHITESPACE
|
||||
|
||||
>>> seq1 = 'abc'
|
||||
>>> seq2 = (1,2,3)
|
||||
>>> [(x,y) for x in seq1 for y in seq2]
|
||||
[('a', 1), ('a', 2), ('a', 3),
|
||||
('b', 1), ('b', 2), ('b', 3),
|
||||
|
@ -441,7 +444,9 @@ variables. But, what if the local variables weren't thrown away on exiting a
|
|||
function? What if you could later resume the function where it left off? This
|
||||
is what generators provide; they can be thought of as resumable functions.
|
||||
|
||||
Here's the simplest example of a generator function::
|
||||
Here's the simplest example of a generator function:
|
||||
|
||||
.. testcode:: doctest_block
|
||||
|
||||
def generate_ints(N):
|
||||
for i in range(N):
|
||||
|
@ -459,11 +464,11 @@ statement is that on reaching a ``yield`` the generator's state of execution is
|
|||
suspended and local variables are preserved. On the next call to the
|
||||
generator's ``.next()`` method, the function will resume executing.
|
||||
|
||||
Here's a sample usage of the ``generate_ints()`` generator::
|
||||
Here's a sample usage of the ``generate_ints()`` generator:
|
||||
|
||||
>>> gen = generate_ints(3)
|
||||
>>> gen
|
||||
<generator object at 0x8117f90>
|
||||
<generator object at ...>
|
||||
>>> gen.next()
|
||||
0
|
||||
>>> gen.next()
|
||||
|
@ -496,9 +501,7 @@ can be much messier.
|
|||
|
||||
The test suite included with Python's library, ``test_generators.py``, contains
|
||||
a number of more interesting examples. Here's one generator that implements an
|
||||
in-order traversal of a tree using generators recursively.
|
||||
|
||||
::
|
||||
in-order traversal of a tree using generators recursively. ::
|
||||
|
||||
# A recursive generator that generates Tree leaves in in-order.
|
||||
def inorder(t):
|
||||
|
@ -553,7 +556,7 @@ returns ``None``.
|
|||
Here's a simple counter that increments by 1 and allows changing the value of
|
||||
the internal counter.
|
||||
|
||||
::
|
||||
.. testcode:: doctest_block
|
||||
|
||||
def counter (maximum):
|
||||
i = 0
|
||||
|
@ -623,14 +626,13 @@ lists instead of iterators.
|
|||
``map(f, iterA, iterB, ...)`` returns a list containing ``f(iterA[0], iterB[0]),
|
||||
f(iterA[1], iterB[1]), f(iterA[2], iterB[2]), ...``.
|
||||
|
||||
::
|
||||
>>> def upper(s):
|
||||
... return s.upper()
|
||||
|
||||
def upper(s):
|
||||
return s.upper()
|
||||
map(upper, ['sentence', 'fragment']) =>
|
||||
>>> map(upper, ['sentence', 'fragment'])
|
||||
['SENTENCE', 'FRAGMENT']
|
||||
|
||||
[upper(s) for s in ['sentence', 'fragment']] =>
|
||||
>>> [upper(s) for s in ['sentence', 'fragment']]
|
||||
['SENTENCE', 'FRAGMENT']
|
||||
|
||||
As shown above, you can achieve the same effect with a list comprehension. The
|
||||
|
@ -643,15 +645,13 @@ comprehensions. A **predicate** is a function that returns the truth value of
|
|||
some condition; for use with :func:`filter`, the predicate must take a single
|
||||
value.
|
||||
|
||||
::
|
||||
>>> def is_even(x):
|
||||
... return (x % 2) == 0
|
||||
|
||||
def is_even(x):
|
||||
return (x % 2) == 0
|
||||
|
||||
filter(is_even, range(10)) =>
|
||||
>>> filter(is_even, range(10))
|
||||
[0, 2, 4, 6, 8]
|
||||
|
||||
This can also be written as a list comprehension::
|
||||
This can also be written as a list comprehension:
|
||||
|
||||
>>> [x for x in range(10) if is_even(x)]
|
||||
[0, 2, 4, 6, 8]
|
||||
|
@ -672,27 +672,27 @@ values at all, a :exc:`TypeError` exception is raised. If the initial value is
|
|||
supplied, it's used as a starting point and ``func(initial_value, A)`` is the
|
||||
first calculation.
|
||||
|
||||
::
|
||||
|
||||
import operator
|
||||
reduce(operator.concat, ['A', 'BB', 'C']) =>
|
||||
>>> import operator
|
||||
>>> reduce(operator.concat, ['A', 'BB', 'C'])
|
||||
'ABBC'
|
||||
reduce(operator.concat, []) =>
|
||||
>>> reduce(operator.concat, [])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: reduce() of empty sequence with no initial value
|
||||
reduce(operator.mul, [1,2,3], 1) =>
|
||||
>>> reduce(operator.mul, [1,2,3], 1)
|
||||
6
|
||||
reduce(operator.mul, [], 1) =>
|
||||
>>> reduce(operator.mul, [], 1)
|
||||
1
|
||||
|
||||
If you use :func:`operator.add` with :func:`reduce`, you'll add up all the
|
||||
elements of the iterable. This case is so common that there's a special
|
||||
built-in called :func:`sum` to compute it::
|
||||
built-in called :func:`sum` to compute it:
|
||||
|
||||
reduce(operator.add, [1,2,3,4], 0) =>
|
||||
>>> reduce(operator.add, [1,2,3,4], 0)
|
||||
10
|
||||
sum([1,2,3,4]) =>
|
||||
>>> sum([1,2,3,4])
|
||||
10
|
||||
sum([]) =>
|
||||
>>> sum([])
|
||||
0
|
||||
|
||||
For many uses of :func:`reduce`, though, it can be clearer to just write the
|
||||
|
@ -710,10 +710,11 @@ obvious :keyword:`for` loop::
|
|||
``enumerate(iter)`` counts off the elements in the iterable, returning 2-tuples
|
||||
containing the count and each element.
|
||||
|
||||
::
|
||||
|
||||
enumerate(['subject', 'verb', 'object']) =>
|
||||
(0, 'subject'), (1, 'verb'), (2, 'object')
|
||||
>>> for item in enumerate(['subject', 'verb', 'object']):
|
||||
... print item
|
||||
(0, 'subject')
|
||||
(1, 'verb')
|
||||
(2, 'object')
|
||||
|
||||
:func:`enumerate` is often used when looping through a list and recording the
|
||||
indexes at which certain conditions are met::
|
||||
|
@ -726,18 +727,16 @@ indexes at which certain conditions are met::
|
|||
``sorted(iterable, [cmp=None], [key=None], [reverse=False)`` collects all the
|
||||
elements of the iterable into a list, sorts the list, and returns the sorted
|
||||
result. The ``cmp``, ``key``, and ``reverse`` arguments are passed through to
|
||||
the constructed list's ``.sort()`` method.
|
||||
the constructed list's ``.sort()`` method. ::
|
||||
|
||||
::
|
||||
|
||||
import random
|
||||
# Generate 8 random numbers between [0, 10000)
|
||||
rand_list = random.sample(range(10000), 8)
|
||||
rand_list =>
|
||||
>>> import random
|
||||
>>> # Generate 8 random numbers between [0, 10000)
|
||||
>>> rand_list = random.sample(range(10000), 8)
|
||||
>>> rand_list
|
||||
[769, 7953, 9828, 6431, 8442, 9878, 6213, 2207]
|
||||
sorted(rand_list) =>
|
||||
>>> sorted(rand_list)
|
||||
[769, 2207, 6213, 6431, 7953, 8442, 9828, 9878]
|
||||
sorted(rand_list, reverse=True) =>
|
||||
>>> sorted(rand_list, reverse=True)
|
||||
[9878, 9828, 8442, 7953, 6431, 6213, 2207, 769]
|
||||
|
||||
(For a more detailed discussion of sorting, see the Sorting mini-HOWTO in the
|
||||
|
@ -746,19 +745,19 @@ Python wiki at http://wiki.python.org/moin/HowTo/Sorting.)
|
|||
The ``any(iter)`` and ``all(iter)`` built-ins look at the truth values of an
|
||||
iterable's contents. :func:`any` returns True if any element in the iterable is
|
||||
a true value, and :func:`all` returns True if all of the elements are true
|
||||
values::
|
||||
values:
|
||||
|
||||
any([0,1,0]) =>
|
||||
>>> any([0,1,0])
|
||||
True
|
||||
any([0,0,0]) =>
|
||||
>>> any([0,0,0])
|
||||
False
|
||||
any([1,1,1]) =>
|
||||
>>> any([1,1,1])
|
||||
True
|
||||
all([0,1,0]) =>
|
||||
>>> all([0,1,0])
|
||||
False
|
||||
all([0,0,0]) =>
|
||||
>>> all([0,0,0])
|
||||
False
|
||||
all([1,1,1]) =>
|
||||
>>> all([1,1,1])
|
||||
True
|
||||
|
||||
|
||||
|
@ -873,17 +872,13 @@ each time. You can optionally supply the starting number, which defaults to 0::
|
|||
|
||||
``itertools.cycle(iter)`` saves a copy of the contents of a provided iterable
|
||||
and returns a new iterator that returns its elements from first to last. The
|
||||
new iterator will repeat these elements infinitely.
|
||||
|
||||
::
|
||||
new iterator will repeat these elements infinitely. ::
|
||||
|
||||
itertools.cycle([1,2,3,4,5]) =>
|
||||
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
|
||||
|
||||
``itertools.repeat(elem, [n])`` returns the provided element ``n`` times, or
|
||||
returns the element endlessly if ``n`` is not provided.
|
||||
|
||||
::
|
||||
returns the element endlessly if ``n`` is not provided. ::
|
||||
|
||||
itertools.repeat('abc') =>
|
||||
abc, abc, abc, abc, abc, abc, abc, abc, abc, abc, ...
|
||||
|
@ -892,9 +887,7 @@ returns the element endlessly if ``n`` is not provided.
|
|||
|
||||
``itertools.chain(iterA, iterB, ...)`` takes an arbitrary number of iterables as
|
||||
input, and returns all the elements of the first iterator, then all the elements
|
||||
of the second, and so on, until all of the iterables have been exhausted.
|
||||
|
||||
::
|
||||
of the second, and so on, until all of the iterables have been exhausted. ::
|
||||
|
||||
itertools.chain(['a', 'b', 'c'], (1, 2, 3)) =>
|
||||
a, b, c, 1, 2, 3
|
||||
|
@ -913,9 +906,7 @@ term for this behaviour is `lazy evaluation
|
|||
|
||||
This iterator is intended to be used with iterables that are all of the same
|
||||
length. If the iterables are of different lengths, the resulting stream will be
|
||||
the same length as the shortest iterable.
|
||||
|
||||
::
|
||||
the same length as the shortest iterable. ::
|
||||
|
||||
itertools.izip(['a', 'b'], (1, 2, 3)) =>
|
||||
('a', 1), ('b', 2)
|
||||
|
@ -929,9 +920,7 @@ slice of the iterator. With a single ``stop`` argument, it will return the
|
|||
first ``stop`` elements. If you supply a starting index, you'll get
|
||||
``stop-start`` elements, and if you supply a value for ``step``, elements will
|
||||
be skipped accordingly. Unlike Python's string and list slicing, you can't use
|
||||
negative values for ``start``, ``stop``, or ``step``.
|
||||
|
||||
::
|
||||
negative values for ``start``, ``stop``, or ``step``. ::
|
||||
|
||||
itertools.islice(range(10), 8) =>
|
||||
0, 1, 2, 3, 4, 5, 6, 7
|
||||
|
@ -945,9 +934,7 @@ independent iterators that will all return the contents of the source iterator.
|
|||
If you don't supply a value for ``n``, the default is 2. Replicating iterators
|
||||
requires saving some of the contents of the source iterator, so this can consume
|
||||
significant memory if the iterator is large and one of the new iterators is
|
||||
consumed more than the others.
|
||||
|
||||
::
|
||||
consumed more than the others. ::
|
||||
|
||||
itertools.tee( itertools.count() ) =>
|
||||
iterA, iterB
|
||||
|
@ -1144,9 +1131,7 @@ This section contains an introduction to some of the most important functions in
|
|||
|
||||
The ``compose()`` function implements function composition. In other words, it
|
||||
returns a wrapper around the ``outer`` and ``inner`` callables, such that the
|
||||
return value from ``inner`` is fed directly to ``outer``. That is,
|
||||
|
||||
::
|
||||
return value from ``inner`` is fed directly to ``outer``. That is, ::
|
||||
|
||||
>>> def add(a, b):
|
||||
... return a + b
|
||||
|
@ -1157,9 +1142,7 @@ return value from ``inner`` is fed directly to ``outer``. That is,
|
|||
>>> compose(double, add)(5, 6)
|
||||
22
|
||||
|
||||
is equivalent to
|
||||
|
||||
::
|
||||
is equivalent to ::
|
||||
|
||||
>>> double(add(5, 6))
|
||||
22
|
||||
|
@ -1169,9 +1152,7 @@ are not always `fully curried <http://en.wikipedia.org/wiki/Currying>`__. By
|
|||
default, it is expected that the ``inner`` function will return a single object
|
||||
and that the ``outer`` function will take a single argument. Setting the
|
||||
``unpack`` argument causes ``compose`` to expect a tuple from ``inner`` which
|
||||
will be expanded before being passed to ``outer``. Put simply,
|
||||
|
||||
::
|
||||
will be expanded before being passed to ``outer``. Put simply, ::
|
||||
|
||||
compose(f, g)(5, 6)
|
||||
|
||||
|
@ -1179,9 +1160,7 @@ is equivalent to::
|
|||
|
||||
f(g(5, 6))
|
||||
|
||||
while
|
||||
|
||||
::
|
||||
while ::
|
||||
|
||||
compose(f, g, unpack=True)(5, 6)
|
||||
|
||||
|
@ -1192,9 +1171,7 @@ is equivalent to::
|
|||
Even though ``compose()`` only accepts two functions, it's trivial to build up a
|
||||
version that will compose any number of functions. We'll use ``reduce()``,
|
||||
``compose()`` and ``partial()`` (the last of which is provided by both
|
||||
``functional`` and ``functools``).
|
||||
|
||||
::
|
||||
``functional`` and ``functools``). ::
|
||||
|
||||
from functional import compose, partial
|
||||
|
||||
|
@ -1212,9 +1189,7 @@ We can also use ``map()``, ``compose()`` and ``partial()`` to craft a version of
|
|||
``flip(func)``
|
||||
|
||||
``flip()`` wraps the callable in ``func`` and causes it to receive its
|
||||
non-keyword arguments in reverse order.
|
||||
|
||||
::
|
||||
non-keyword arguments in reverse order. ::
|
||||
|
||||
>>> def triple(a, b, c):
|
||||
... return (a, b, c)
|
||||
|
@ -1260,8 +1235,7 @@ We can use ``foldl()``, ``operator.concat()`` and ``partial()`` to write a
|
|||
cleaner, more aesthetically-pleasing version of Python's ``"".join(...)``
|
||||
idiom::
|
||||
|
||||
from functional import foldl, partial
|
||||
from operator import concat
|
||||
from functional import foldl, partial from operator import concat
|
||||
|
||||
join = partial(foldl, concat, "")
|
||||
|
||||
|
|
Loading…
Reference in New Issue