Подтвердить что ты не робот

Питонический эквивалент смещения или повтора?

Я изучаю Python, и у меня есть ситуация, когда я хочу потреблять элементы из итератора. Трудная часть заключается в том, что при определенных условиях я хочу "un-итерации". То есть, положите элемент обратно на переднюю часть итератора до того, как я зациклится.

Например, предположим, что я собираю яблоки из дерева. Моя корзина с фруктами может содержать только 10 кг, прежде чем ее нужно опорожнить. Но я должен выбрать каждое яблоко, прежде чем я смогу взвесить его, и определить, будет ли это яблоко превышать емкость корзины.

На языке, подобном Perl, я мог бы unshift() вернуть яблоко на дерево, а затем позволить выражению цикла перебрать яблоко:

while ($apple = shift(@tree)) {
  $wt = weight($apple);
  if ($wt + weight(@basket) > 10) {
    send(@basket);
    @basket = ();
    unshift(@tree, $apple);
  } else {
    push(@basket, $element);
  }
}

Или еще я могу использовать redo, который возобновляет обработку в верхней части блока, не оценивая выражение цикла. Таким образом, одно и то же яблоко может быть переработано после опустошения корзины.

while ($apple = shift(@tree)) {
  $wt = weight($apple);
  if ($wt + weight(@basket) > 10) {
    send(@basket);
    @basket = ();
    redo;
  } else {
    push(@basket, $apple);
  }
}

Каким будет наиболее pythonic решение для такого рода проблем?

4b9b3361

Ответ 1

Зачем беспокоиться о неперемещении, когда всегда должно существовать предложение else?

for apple in tree:
    if (apple.weight + basket.weight) > 10:
       send(basket)
       basket.clear()
    basket.add(apple)

В любом случае, я уверен, что у Python нет такого поведения, которое вы ищете.

Ответ 2

Я изучаю Python, и у меня есть ситуация, когда я хочу потреблять элементы из итератора. Трудная часть заключается в том, что при определенных условиях я хочу "un-итерации". То есть, положите элемент обратно на переднюю часть итератора до того, как я зациклится.

Здесь простое решение:

class MyIterator(object):   # undo-able iterator wrapper
    def __init__(self, iterable):
        super(MyIterator, self).__init__()
        self.iterator = iter(iterable)
        self.stack = []

    def __iter__(self):
        return self

    def next(self):
        if self.stack:
            return self.stack.pop()
        return self.iterator.next()  # Raises StopIteration eventually

    def undo(self, item):
        self.stack.append(item)
for i in  MyIterator(xrange(5)): print i
0
1
2
3
4
rng = MyIterator(xrange(5))
rng.next()
0
rng.next()
1
rng.undo(1)
rng.next()
1

Ответ 3

Я бы сказал, что наиболее Pythonic-решение является самым простым. Вместо того, чтобы пытаться обернуть итератор в выражении генератора, который позволяет вам "отступить" или что-то подобное, используйте цикл while, как и в Perl! Итераторы не очень хорошо смешиваются с мутацией, anywho.

Простой перевод вашей реализации (игнорирование оптимизации @Patrick):

while tree:
    apple = tree.pop(0)
    if apple.weight + basket.weight > 10:
        basket.send()
        basket.clear()
        tree.insert(0, apple) # Put it back.
    else:
        basket.append(apple)

Или вы можете использовать peek -подобную функциональность с упорядоченными индексами последовательности:

while tree:
    apple = tree[0] # Take a peek at it.
    if apple.weight + basket.weight > 10:
        basket.send()
        basket.clear()
    else:
        basket.append(tree.pop(0))

Если вам не нравится аргумент "simple", проверьте тетера collections.deque, упомянутые в приведенном выше (связанном) потоке.

Ответ 4

Если вы не хотите следовать другому предложению просто удалить предложение else, вы можете написать свою собственную функцию unshift, которая будет работать аналогично perl с любым итерабельным:

class UnshiftableIterable(object):
    def __init__(self, iterable):
        self._iter = iter(iterable)
        self._unshifted = [] # empty list of unshifted stuff
    def __iter__(self):
        while True:
            if self._unshifted:
                yield self._unshifted.pop()
            else:
                yield self._iter.next()
    def unshift(self, item):
        self._unshifted.append(item)

Затем в вашем коде:

it = UnshiftableIterable(tree)
for apple in tree:
    if weigth(basket) + weight(apple) > MAX_WEIGHT:
        send(basket)
        basket = []
        it.unshift(apple)
    else:
        basket.append(apple)

Некоторые тесты UnshiftableIterable:

it = UnshiftableIterable(xrange(5))

for i in it:
    print '*',
    if i == 2:
        it.unshift(10)
    else:
        print i,
# output: * 0 * 1 * * 10 * 3 * 4

Ответ 6

Пока я писал это @Patrick уже предложил то же самое. Но поскольку я написал это, я все равно вставляю код, с комментариями в методах маркировки кода от Патрика.

import random

apples=[random.randint(1,3) for j in range(10)]
print 'apples',apples

basket=[]
y=6
baskets=[]

for i in range(len(apples)):
    if sum(basket+[apples[i]])>y:
        #basket is full                                                                                                                                     
        baskets.append(basket)#basket.send()                                                                                                                
        basket=[]#basket.empty()                                                                                                                            
    basket.append(apples[i])#add apple to basket                                                                                                            

print 'baskets',baskets

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

вывод

apples [1, 1, 3, 3, 1, 1, 3, 3, 2, 3]
baskets [[1, 1, 3], [3, 1, 1], [3, 3]]

Ответ 7

Кстати, вам действительно нужно list.insert(0, yourObject)

Ответ 8

Возвращаясь к исходному вопросу о impementing unshift, operator.delitem можно использовать для реализации простой функции, отличной от OO:

from operator import delitem

def unshift(l,idx):
    retval = l[0]
    delitem(l,0)
    return retval

x = [2,4,6,8]

firstval = unshift(x,0)

print firstval,x

2 [4, 6, 8]

Ответ 9

Нет способа общего способа вставить значение в итератор в python. Лучше всего подходит стек или связанный список.

Если вы повторяете список или что-то в этом роде, конечно, вы можете добавить элемент вручную обратно в список. Но вы также можете перебирать объекты, которые нельзя манипулировать таким образом.

Если вы хотите использовать python для реализации этого алгоритма, вам нужно будет выбрать структуру данных, которая позволяет выполнять операции, которые вы хотите использовать. Я предлагаю методы .push() и .pop(), которые позволяют обрабатывать списки как стеки.