mapLast, mapFirst, and just general iterator questions

Travis Griggs travisgriggs at gmail.com
Tue Jun 14 14:05:52 EDT 2022


I want to be able to apply different transformations to the first and last elements of an arbitrary sized finite iterator in python3. It's a custom iterator so does not have _reversed_. If the first and last elements are the same (e.g. size 1), it should apply both transforms to the same element. I'm doing this because I have an iterator of time span tuples, and I want to clamp the first and last elements, but know any/all of the middle values are inherently in range.

A silly example might be a process that given an iterator of strings, chops the the outer characters off of the value, and uppercases the final value. For example:


def iterEmpty():
    return iter([])

def iter1():
    yield "howdy"

def iter2():
    yield "howdy"
    yield "byebye"

def iterMany():
    yield "howdy"
    yield "hope"
    yield "your"
    yield "day"
    yield "is"
    yield "swell"
    yield "byebye"

def mapFirst(stream, transform):
    try:
        first = next(stream)
    except StopIteration:
        return
    yield transform(first)
    yield from stream

def mapLast(stream, transform):
    try:
        previous = next(stream)
    except StopIteration:
        return
    for item in stream:
        yield previous
        previous = item
    yield transform(previous)

def main():
    for each in (iterEmpty, iter1, iter2, iterMany):
        baseIterator = each()
        chopFirst = mapFirst(baseIterator, lambda x: x[1:-1])
        andCapLast = mapLast(chopFirst, lambda x: x.upper())
        print(repr(" ".join(andCapLast)))


This outputs:

''
'OWD'
'owd BYEBYE'
'owd hope your day is swell BYEBYE'

Is this idiomatic? Especially my implementations of mapFirst and mapList there in the middle? Or is there some way to pull this off that is more elegant?

I've been doing more with iterators and stacking them (probably because I've been playing with Elixir elsewhere), I am generally curious what the performance tradeoffs of heavy use of iterators and yield functions in python is. I know the argument for avoiding big list copies when moving between stages. Is it one of those things where there's also some overhead with them, where for small stuff, you'd just be better list-ifying the first iterator and then working with lists (where, for example, I could do the first/last clamp operation with just indexing operations).


More information about the Python-list mailing list