##
6.5.3 Recipes

This section shows recipes for creating an extended toolset using the existing itertools as building blocks.

The extended tools offer the same high performance as the underlying toolset. The superior memory performance is kept by processing elements one at a time rather than bringing the whole iterable into memory all at once. Code volume is kept small by linking the tools together in a functional style which helps eliminate temporary variables. High speed is retained by preferring ``vectorized'' building blocks over the use of for-loops and generators which incur interpreter overhead.

    def take(n, seq):
        return list(islice(seq, n))

    def enumerate(iterable):
        return izip(count(), iterable)

    def tabulate(function):
        "Return function(0), function(1), ..."
        return imap(function, count())

    def iteritems(mapping):
        return izip(mapping.iterkeys(), mapping.itervalues())

    def nth(iterable, n):
        "Returns the nth item or raise StopIteration"
        return islice(iterable, n, None).next()

    def all(seq, pred=None):
        "Returns True if pred(x) is true for every element in the iterable"
        for elem in ifilterfalse(pred, seq):
            return False
        return True

    def any(seq, pred=None):
        "Returns True if pred(x) is true for at least one element in the iterable"
        for elem in ifilter(pred, seq):
            return True
        return False

    def no(seq, pred=None):
        "Returns True if pred(x) is false for every element in the iterable"
        for elem in ifilter(pred, seq):
            return False
        return True

    def quantify(seq, pred=None):
        "Count how many times the predicate is true in the sequence"
        return sum(imap(pred, seq))

    def padnone(seq):
        """Returns the sequence elements and then returns None indefinitely.

        Useful for emulating the behavior of the built-in map() function.
        """
        return chain(seq, repeat(None))

    def ncycles(seq, n):
        "Returns the sequence elements n times"
        return chain(*repeat(seq, n))

    def dotproduct(vec1, vec2):
        return sum(imap(operator.mul, vec1, vec2))

    def flatten(listOfLists):
        return list(chain(*listOfLists))

    def repeatfunc(func, times=None, *args):
        """Repeat calls to func with specified arguments.

        Example:  repeatfunc(random.random)
        """
        if times is None:
            return starmap(func, repeat(args))
        else:
            return starmap(func, repeat(args, times))

    def pairwise(iterable):
        "s -> (s0,s1), (s1,s2), (s2, s3), ..."
        a, b = tee(iterable)
        for elem in b:
            break
        return izip(a, b)

    def grouper(n, iterable, padvalue=None):
        "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
        return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

    def reverse_map(d):
        "Return a new dict with swapped keys and values"
        return dict(izip(d.itervalues(), d))

    def roundrobin(*iterables):
        "roundrobin('abc', 'd', 'ef') --> 'a', 'd', 'e', 'b', 'f', 'c'"
        # Recipe credited to George Sakkis
        pending = len(iterables)
        nexts = cycle(iter(it).next for it in iterables)
        while pending:
            try:
                for next in nexts:
                    yield next()
            except StopIteration:
                pending -= 1
                nexts = cycle(islice(nexts, pending))

    def powerset(iterable):
        "powerset('ab') --> set([]), set(['a']), set(['b']), set(['a', 'b'])"
        # Recipe credited to Eric Raymond
        pairs = [(2**i, x) for i, x in enumerate(iterable)]
        for n in xrange(2**len(pairs)):
            yield set(x for m, x in pairs if m&n)