Генератор, который оставляет заполнитель в начале и в конце входного итератора без изменений - PullRequest
3 голосов
/ 13 октября 2011

Давайте рассмотрим список в качестве примера:

a = [255, 255, 1, 255, 255, 255, 1, 2, 255, 255, 2, 255, 255, 3, 255, 3, 255, 255, 255]

255 - это особое значение в нем. Это заполнитель.

Я создал генератор, который заменит часть заполнителя в списке. Работает как положено.

Но мне не нужно обрабатывать начальные заполнители [255, 255 и конечные заполнители 255, 255, 255] и выводить их без изменений.

Итак, я попытался изменить генератор, чтобы он работал:

Python 2.7

from __future__ import print_function
from  itertools import tee, izip, ifilterfalse

def replace(iterable,placeholder=255):
    it = enumerate(iterable) #the position is needed for the logic for the middle of the list
    it = ifilterfalse(lambda x: x[1]==placeholder, it) #create an iterator that deletes all the placeholders
    for i,(left,right) in enumerate(window(it,2)): #Slide through the filtered list with the window of 2 elements
        if i==0: #Leaving the beginning placeholders intact
            for j in range(left[0]):
                yield placeholder

        #SOME LOGIC FOR THE MIDDLE OF THE LIST (it works well)

    #Need to leave the trailing placeholders intact.

Промежуточные значения, преобразованные в список, просто для облегчения понимания кода:

>>>iterable
[255,1,255,255,1,255,255,255,2,2,255,255,255,2,2,3,255,255,255,3,255,255]

>>>it = enumerate(iterable)
[(0, 255), (1, 1), (2, 255), (3, 255), (4, 1), (5, 255), (6, 255), (7, 255), (8, 2), (9, 2), (10, 255), (11, 255), (12, 255), (13, 2), (14, 2), (15, 3), (16, 255), (17, 255), (18, 255), (19, 3), (20, 255), (21, 255)]

>>>it = ifilterfalse(lambda x: x[1]==placeholder, it)
[(1, 1), (4, 1), (8, 2), (9, 2), (13, 2), (14, 2), (15, 3), (19, 3)]

>>>list(enumerate(window(it,2)))
[(0, ((1, 1), (4, 1))), (1, ((4, 1), (8, 2))), (2, ((8, 2), (9, 2))), (3, ((9, 2), (13, 2))), (4, ((13, 2), (14, 2))), (5, ((14, 2), (15, 3))), (6, ((15, 3), (19, 3)))]

Итак, как вы можете видеть, list(enumerate(window(it,2))) содержит индекс начального значения без заполнителя (0, ((**1**, 1), (4, 1))),, но не содержит информацию о том, сколько конечных заполнителей было у исходного итератора: list(enumerate(window(it,2))) заканчивается это значение (6, ((15, 3), (**19**, 3))), которое имеет только индекс последнего значения, не являющегося заполнителем, которое не дает информации о количестве оставшихся заполнителей.

Мне удалось обработать ведущие заполнители, полагаясь на it = enumerate(iterable), который возвращает позицию начального значения итератора, которое сохраняется в первом полученном значении, на ifilterfalse.

Но я потратил довольно много времени, пытаясь понять, как сделать то же самое с конечными заполнителями. Проблема в том, что ifilterfalse просто глотает последние значения заполнителей enumerate(iterable), и я не вижу способа получить к ним доступ (это было возможно для ведущих заполнителей, поскольку первое сгенерированное значение ifilterfalse содержало индекс значения enumerate(iterable)).

Вопрос

Каков наилучший способ исправить этот код, чтобы он обрабатывал конечные заполнители?

Поскольку цель состоит не в том, чтобы создать код всеми средствами (я уже сделал это , используя другую технику), я хочу решить задачу, немного повозившись с кодом, а не полностью переписать его.

Это больше тренировка, чем реальная задача.

Дополнительная информация

window - это код от здесь .

Мой код работает почти так же, как в этом ответе @ nye17. Но в этом коде автор вносит на месте изменения исходного списка. И я хочу создать генератор, который будет давать те же значения, что и результирующий список в этом коде.

Кроме того, я хочу, чтобы мой генератор принимал любые итерации в качестве параметра, а не только списки (например, он может принимать итератор, который читает значения из файла одно за другим). Имея только списки в качестве параметра, задача упрощается, поскольку мы можем сканировать список с конца.

Это не настоящая задача, которую мне приходится решать в жизни. Это просто для тренировки.

Полный код http://codepad.org/9UJ9comY

Ответы [ 3 ]

2 голосов
/ 13 октября 2011
def replace(it, process, placeholder):
    it = iter(it)
    while True:
        item = it.next()
        if item == placeholder:
            yield item
        else:
            yield process(item)
    pcount = 0
    try:
        while True:
            item = it.next()
            if item == placeholder:
                pcount += 1
            else:
                for i in range(pcount):
                    yield process(placeholder)
                pcount = 0
                yield process(item)
    except StopIteration:
        for i in range(pcount):
            yield placeholder

Используйте это так:

>>> a = [0, 0, 1, 0, 0, 0, 1, 2, 0, 0, 2, 0, 0, 3, 0, 3, 0, 0, 0]
>>> [x for x in replace(a, lambda n: n+20, 0)]
[0, 0, 21, 20, 20, 20, 21, 22, 20, 20, 22, 20, 20, 23, 20, 23, 0, 0, 0]
0 голосов
/ 15 октября 2011

Самое простое решение, которое я нашел, - это обработать it = enumerate(iterable) через еще один генератор, который просто сохраняет последнее возвращаемое значение.

Итак, я добавил следующий код после it = enumerate(iterable) (внутри функции replace):

def save_last(iterable):
        for i in iterable:
            yield i
        replace.last_index = i[0] #Save the last value
it = save_last(it)

После того, как iterable исчерпан, последний оператор генератора сохраняет индекс полученного значения (который равен i[0], поскольку enumerate сохраняет его в позиции 0 кортежа) как атрибут replace (поскольку функция replace является экземпляром класса, который может иметь локальные переменные).

it обернут во вновь созданный генератор save_last.

В самом конце функции я добавил код, который использует сохраненный индекс в переменной replace.last_index.

if right[0]<replace.last_index:
    for i in range(replace.last_index-right[0]):
        yield placeholder

Полный код:

from __future__ import print_function
from  itertools import tee, izip, ifilterfalse


def window(iterable,n):
    els = tee(iterable,n)
    for i,el in enumerate(els):
        for _ in range(i):
            next(el, None)
    return izip(*els)


def replace(iterable,placeholder=255):
    it = enumerate(iterable)

    def save_last(iterable):
        for i in iterable:
            yield i
        replace.last_index = i[0] #Save the last value
    it = save_last(it)

    it = ifilterfalse(lambda x: x[1]==placeholder, it)
    for i,(left,right) in enumerate(window(it,2)):
        if i==0:
            for j in range(left[0]):
                yield placeholder
        yield left[1]
        if right[0]>left[0]+1:
            if left[1]==right[1]:
                for _ in range(right[0]-left[0]-1):
                    yield left[1]
            else:
                for _ in range(right[0]-left[0]-1):
                    yield placeholder
    yield right[1]
    if right[0]<replace.last_index:
        for i in range(replace.last_index-right[0]):
            yield placeholder


a = [255,1,255,255,1,255,255,255,2,2,255,255,255,2,2,3,255,255,255,3,255,255]        
print('\nInput: {}'.format(a))
output = list(replace(a))
print('Proram output: {}'.format(output))
print('Goal output  : {}'.format([255,1,1,1,1,255,255,255,2,2,2,2,2,2,2,3,3,3,3,3,255,255]))

, который работает как ожидалось:

Input: [255, 1, 255, 255, 1, 255, 255, 255, 2, 2, 255, 255, 255, 2, 2, 3, 255, 255, 255, 3, 255, 255]
Proram output: [255, 1, 1, 1, 1, 255, 255, 255, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 255, 255]
Goal output  : [255, 1, 1, 1, 1, 255, 255, 255, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 255, 255]

Единственное, что мне не нравится, это сочетание очень эффективных написанных на C ifilterfalse и save_last написанных на Python.

0 голосов
/ 13 октября 2011
def replace(it, placeholder):
    while True:
        curr = it.next()
        if curr == placeholder:
            yield curr
        else:
            break

    yield curr

    try:
        cache = []
        while True:      
            curr = it.next()

            if curr == placeholder:
                cache.append(curr)
            else:
                for cached in cache:
                    yield TRANSFORM(cached)
                yield curr
                cache = []

    except StopIteration:
        for cached in cache:
            yield cache
...