简体   繁体   English

在python中模拟C风格的循环

[英]Simulating C-style for loops in python

(even the title of this is going to cause flames, I realize) (即使这个标题会导致火焰,我意识到)

Python made the deliberate design choice to have the for loop use explicit iterables, with the benefit of considerably simplified code in most cases. Python做出了深思熟虑的设计选择,让for循环使用显式迭代,在大多数情况下,代码大大简化了。

However, sometimes it is quite a pain to construct an iterable if your test case and update function are complicated, and so I find myself writing the following while loops: 但是,如果你的测试用例和更新函数很复杂,有时候构造一个iterable会很麻烦,所以我发现自己编写了以下while循环:

val = START_VAL
while <awkward/complicated test case>:
    # do stuff
    ...
    val = <awkward/complicated update>

The problem with this is that the update is at the bottom of the while block, meaning that if I want to have a continue embedded somewhere in it I have to: 这个问题是更新位于while块的底部,这意味着如果我想在其中嵌入一个continue ,我必须:

  • use duplicate code for the complicated/awkard update, AND 使用重复代码进行复杂/ awkard更新,AND

  • run the risk of forgetting it and having my code infinite loop 冒着忘记它并让我的代码无限循环的风险

I could go the route of hand-rolling a complicated iterator: 我可以去手动滚动一个复杂的迭代器:

def complicated_iterator(val):
    while <awkward/complicated test case>:
         yeild val
         val = <awkward/complicated update>

for val in complicated_iterator(start_val):
    if <random check>:
         continue # no issues here
    # do stuff

This strikes me as waaaaay too verbose and complicated. 这让我感到震惊,因为waaaaay太冗长和复杂。 Do folks in stack overflow have a simpler suggestion? 堆栈溢出中的人有一个更简单的建议吗?

Response to comments: 回复评论:

@Glenn Maynard: Yes, I dismissed the answer. @Glenn Maynard:是的,我驳回了答案。 It's bad to write five lines if there is a way to do it in one... especially in a case that comes up all the time (looping being a common feature of Turing-complete programs). 如果有一种方法可以在一个方法中编写五行,那就太糟糕了......特别是在一直出现的情况下(循环是图灵完成程序的一个共同特征)。

For the folks looking for a concrete example: let's say I'm working with a custom date library. 对于寻找具体示例的人:假设我正在使用自定义日期库。 My question would then be, how would you express this in python: 那么我的问题是,你将如何在python中表达这一点:

for (date = start; date < end; date = calendar.next_quarter_end(date)):
    if another_calendar.is_holiday(date):
       continue
    # ... do stuff...

This is the best I can come up with: 这是我能想到的最好的:

def cfor(first,test,update):
    while test(first):
        yield first
        first = update(first)

def example(blah):
    print "do some stuff"
    for i in cfor(0,lambda i:i<blah,lambda i:i+1):
        print i
    print "done"

I wish python had a syntax for closured expressions. 我希望python有一个闭包表达式的语法。

Edit: Also, note that you only have to define cfor once (as opposed to your complicated_iterator function). 编辑:另外,请注意,您只需要定义一次cfor(而不是您的complicated_iterator函数)。

I'm a little confused: you have a complicated while expression, and a complicated next expression, but they fit nicely into a C for loop? 我有点困惑:你有一个复杂的while表达式,一个复杂的下一个表达式,但它们很适合C for循环? That doesn't make sense to me. 这对我来说没有意义。

I recommend the custom iterator approach. 我推荐自定义迭代器方法。 You will likely find other uses for the iterator, and encapsulating the iteration is good practice anyway. 您可能会发现迭代器的其他用途,无论如何封装迭代都是很好的做法。

UPDATE: Using your example, I would definitely make a custom iterator. 更新:使用您的示例,我肯定会创建一个自定义迭代器。 It seems perfectly natural to me that a calendar would be able to generate a series of quarterly dates: 对我来说,日历能够产生一系列季度日期似乎是很自然的:

class Calendar:
    # ...

    def quarters(self, start, end):
        """Generate the quarter-start dates between `start` and `end`."""
        date = start
        while date < end:
            yield date
            date = self.next_quarter_end(date)


for date in calendar.quarters(start, end):
    if another_calendar.is_holiday(date):
       continue
    # ... do stuff...

This seems like a wonderful abstraction for your calendar class to provide, and I bet you'll use it more than once. 这似乎是您的日历类提供的一个很棒的抽象,我打赌你会不止一次地使用它。

What about: 关于什么:

date = start

while date < end:

    if not another_calendar.is_holiday(date):
        # ... do stuff...

    date = calendar.next_quarter_end(date)

But if you use that particular construct often, you're better off defining the generator once and re-using it as you did in your question. 但是如果你经常使用那个特定的构造,你最好定义一次生成器并像你在问题中那样重新使用它。

(The fact is, since they're different languages, you can't possibly have every construct in C map to a more compact construct in Python. It's like claiming to have a compression algorithm that works equally well on all random inputs.) (事实是,由于它们是不同的语言,你不可能将C映射中的每个构造都用于Python中更紧凑的构造。它就像声称有一个压缩算法在所有随机输入上同样有效。)

You could use a try/finally clause to execute the update: 您可以使用try / finally子句来执行更新:

val = START_VAL

while <awkward/complicated test case>:
    try:
        # do stuff
        continue

    finally:
        val = <awkward/complicated update>

Caveat: this will also execute the update statement if you do a break . 警告:如果你break这也会执行更新语句。

I often do 我经常这样做

while True:
   val = <awkward/complicated update>
   if not val:
     break

   etc.

Heh: 嘿:

def forre(a,i,c,top,increment,run):
    increment = increment.replace("++","+=1").replace("--","-=1").replace("; ","")
    while i != top:
        try: exec(run)
        except: print "error: "; print run
        try: exec(increment)
        except: print "error: "; print increment

forre("int i=",0,"; i<",6,"; i++", 
    "print i"
    )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM