Pythonic 相当于 unshift 或 redo?

发布于 2024-07-11 20:56:48 字数 840 浏览 9 评论 0原文

我正在学习 Python,我遇到了一种情况,我想使用迭代器中的项目。 棘手的部分是,在某些条件下,我想“取消迭代”。 也就是说,在循环之前将一个项目放回到迭代器的前面。

例如,假设我正在从树上摘苹果。 我的果篮只能装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的解决方案是什么?

I'm learning Python, and I have a situation where I want to consume items from an iterator. The tricky part is that under certain conditions, I want to "un-iterate." That is, put an item back onto the front of the iterator before I loop.

For example, suppose I'm picking apples from a tree. My fruit basket can only hold 10kg before it needs to be emptied. But I have to pick each apple before I can weigh it and determine if this apple would exceed the capacity of the basket.

In a language like Perl, I could unshift() the apple back onto the tree, and then let the loop expression re-pick the apple:

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

Or else I can also use redo, which resumes processing at the top of block, without evaluating the loop expression. So the same apple can be re-processed, after the basket has been emptied.

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

What would be the most pythonic solution for this kind of problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

猫烠⑼条掵仅有一顆心 2024-07-18 20:56:48

我正在学习 Python,我遇到了一种情况,我想从迭代器中使用项目。 棘手的部分是,在某些条件下,我想“取消迭代”。 也就是说,在循环之前将一个项目放回迭代器的前面。

这是一个简单的解决方案:

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

I'm learning Python, and I have a situation where I want to consume items from an iterator. The tricky part is that under certain conditions, I want to "un-iterate." That is, put an item back onto the front of the iterator before I loop.

Here's a simple solution:

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
手心的温暖 2024-07-18 20:56:48

当 else 子句应该总是出现时,为什么还要费心保持不变呢?

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

不管怎样,我相当确定 Python 不具备您正在寻找的那种行为。

Why bother with unshifting when the else clause should always occur?

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

Anyway, I'm fairly certain that Python doesn't have the sort of behavior you're looking for.

笨笨の傻瓜 2024-07-18 20:56:48

我想说最Pythonic的解决方案是最简单的< /a>. 不要尝试将迭代器包装在允许您“回溯”或类似复杂的东西的生成器表达式中,而是使用 while 循环,就像在 Perl 中一样! 迭代器与突变不能很好地混合,任何人。

简单翻译您的实现(忽略 @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))

如果您不喜欢“简单”参数,请查看中提到的collections.deque迭代器上面(链接的)线程。

I'd say that the most Pythonic solution is the simplest one. Instead of trying to wrap an iterator in a generator expression that allows you to "backtrack" or something similarly complex, use a while loop, as you have in Perl! Iterators don't mix very nicely with mutation, anywho.

Simple translation of your implementation (ignoring @Patrick's optimization):

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)

Or, you could use a peek-like functionality with ordered sequence indices:

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))

If you don't like the "simple" argument, check out the collections.deque iterators mentioned in the above (linked) thread.

风向决定发型 2024-07-18 20:56:48

如果您不想遵循其他人的建议,仅删除 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

If you don't want to follow the other's suggestion of just removing the else clause, you can write your own unshift function that will work in a way similar to perl's with any iterable:

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)

Then in your code:

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

Some testing of the 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
Oo萌小芽oO 2024-07-18 20:56:48

您正在寻找一个生成器,一个可以通过 send() 方法接收对其内部状态的修改的迭代器

https://docs.python.org/howto/function.html#passing-values-into-a-generator

You're looking for a generator, an iterator that can receive modifications to its internal state via the send() method

https://docs.python.org/howto/functional.html#passing-values-into-a-generator

尐偏执 2024-07-18 20:56:48

顺便说一句,你真正想要的是 list.insert(0,yourObject)

By the way, what you really want is list.insert(0,yourObject)

灼疼热情 2024-07-18 20:56:48

当我写这篇文章时,@Patrick 已经提出了同样的建议。 但既然我已经写好了,我无论如何都会粘贴代码,并在代码标记方法中添加来自 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]]

While I was writing this @Patrick already suggested the same thing. But since I have written it I will paste the code anyways, with comments in code marking methods from 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

though this does not pop() the apples from the original iterator. Please remark if that's a desired behavior too.

the output

apples [1, 1, 3, 3, 1, 1, 3, 3, 2, 3]
baskets [[1, 1, 3], [3, 1, 1], [3, 3]]
我恋#小黄人 2024-07-18 20:56:48

目前我的升级版本 pythonizer 无法处理 redo 但如果我添加它,我可能会像这样实现它:(

while (apple:=(tree.pop(0) if tree else None)):
    while True:
        wt = weight(apple)
        if wt+weight(*basket) > 10:
            sendit(basket)
            basket = []
            continue
        else:
            basket.append(apple)
        break

注意:我必须将 send 更改为 sendit 因为 send 是在 perl 中预定义的。)

Currently my upgraded version of pythonizer doesn't handle redo but if I added it, I would probably implement it like this:

while (apple:=(tree.pop(0) if tree else None)):
    while True:
        wt = weight(apple)
        if wt+weight(*basket) > 10:
            sendit(basket)
            basket = []
            continue
        else:
            basket.append(apple)
        break

(Note: I had to change send to sendit because send is predefined in perl.)

神经暖 2024-07-18 20:56:48

回到最初关于实现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]

Back to the original question about impementing unshift, operator.delitem can be used to implement a simple non-OO function:

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]

流心雨 2024-07-18 20:56:48

在 python 中,没有通用的方法可以将值推入迭代器。 堆栈或链表更适合于此。

如果您要迭代列表或其他内容,当然您可以手动将项目添加回列表中。 但您也可以迭代无法以这种方式操作的对象。

如果你想使用 python 来实现该算法,你必须选择一个允许你想要使用的操作的数据结构。 我建议使用 .push().pop() 方法,它们可以让您将列表视为堆栈。

There is no way general way to push a value into an iterator in python. A stack or linked list is better suited to that.

If you're iterating over a list or something, of course you can add the item manually back to the list. But you can also iterate over objects which can't be manipulated in such a way.

If you want to use python to implement that algorithm, you'll have to choose a data structure that allows the operations you want to use. I suggest the .push() and .pop() methods which let you treat lists as stacks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文