HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Functions taking iterables: peaks, compress, and skipper

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
iterablescompressskipperandfunctionspeakstaking

Problem

I am just testing out a few small functions for learning purposes:

def peaks(iterable):
    # returns a list of int for those values in the iterable that are bigger
    # than the value preceding and following them.
    itr = iter(iterable)
    peak =[]
    curr = next(itr)
    last = next(itr)
    try:
        while True:
            first = curr
            curr = last
            last = next(itr)
            if curr > first and curr > last:
                peak.append(curr)
    except:
        pass    
    return peak

def compress(v_iterable,b_iterable):
    #takes two iterables as parameters: it produces every value from the first iterable that
    # has its equivalent position in the second iterable representing what Python would consider
    # a True value. Terminate the iteration when either iterable terminates
    mega_it = dict(zip(v_iterable, b_iterable))
    for nxt in sorted(mega_it):
        if mega_it[nxt]:
            yield nxt

def skipper(iterable,n=0):
    #akes one iterable and one int (whose default value is 0) as parameters: it produces values from
    # the iterable, starting at the first one, skipping the number of values specified by the int
    #(skipping 0 means producing every value from the iterable)
    itr = iter(iterable)
    yield next(itr)
    while True:
        for i in range(n):
            skipped = next(itr)
        yield next(itr)


I feel like my codes are lengthy for the kind of work it does. Is there a way to make my functions cleaner or smaller?

Solution

Your comments are good, but they should all be written as docstrings instead.

When working with iterators, itertools and generator expressions are your friend. peaks() works with three iterators, so zip() is useful.

Since the function accepts an iterable, it seems fitting that it should be a generator rather than returning a list.

Python supports double-ended inequalities.

from itertools import tee

def peaks(iterable):
    """Generates values in the iterable that are greater than the value
    preceding and following them."""
    before, this, after = tee(iter(iterable), 3)
    next(this)
    next(after)
    next(after)
    for prev, curr, succ in zip(before, this, after):
        if prev  succ:
            yield curr


If you wanted to keep the old interface, which returns a list, then a list comprehension would be a good way to write it.

def peaks(iterable):
    """Returns a list of values in the iterable that are greater than the
    preceding and following value."""
    xs, ys, zs = tee(iter(iterable), 3)
    next(ys)
    next(zs)
    next(zs)
    return [y for (x, y, z) in zip(xs, ys, zs) if x  z]


I find your compress() weird, in that it sorts the results.

You can implement the function using just a generator expression.

def compress(values, incl_excl):
    """Keeping only values whose corresponding entry in incl_excl is true,
    generates the sorted filtered list."""
    yield from sorted(val for val, include in zip(values, incl_excl) if include)


In skipper(), the two occurrences of yield next(itr) should be combined.

def skipper(iterable, n=0):
    """Generator that yields the first element, then skips n values between
    results, until the input is exhausted."""
    iterable = iter(iterable)
    while True:
        yield next(iterable)
        for _ in range(n):
            next(iterable)

Code Snippets

from itertools import tee

def peaks(iterable):
    """Generates values in the iterable that are greater than the value
    preceding and following them."""
    before, this, after = tee(iter(iterable), 3)
    next(this)
    next(after)
    next(after)
    for prev, curr, succ in zip(before, this, after):
        if prev < curr > succ:
            yield curr
def peaks(iterable):
    """Returns a list of values in the iterable that are greater than the
    preceding and following value."""
    xs, ys, zs = tee(iter(iterable), 3)
    next(ys)
    next(zs)
    next(zs)
    return [y for (x, y, z) in zip(xs, ys, zs) if x < y > z]
def compress(values, incl_excl):
    """Keeping only values whose corresponding entry in incl_excl is true,
    generates the sorted filtered list."""
    yield from sorted(val for val, include in zip(values, incl_excl) if include)
def skipper(iterable, n=0):
    """Generator that yields the first element, then skips n values between
    results, until the input is exhausted."""
    iterable = iter(iterable)
    while True:
        yield next(iterable)
        for _ in range(n):
            next(iterable)

Context

StackExchange Code Review Q#79393, answer score: 3

Revisions (0)

No revisions yet.