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

Имитация стиля C для петель в python

(даже название этого будет создавать пламя, я понимаю)

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

Однако иногда бывает сложно создать итерабельность, если ваш тестовый пример и функция обновления сложны, и поэтому я нахожу, что пишу следующие циклы:

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

Проблема заключается в том, что обновление находится в нижней части блока while, что означает, что если я хочу, чтобы в нем был встроен continue, я должен:

  • используйте повторяющийся код для сложного/awkard-обновления, AND

  • запускайте риск забыть об этом и имея бесконечный цикл кода

Я мог бы пройти маршрут ручной смены сложного итератора:

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

Это поражает меня как waaaaay слишком многословным и сложным. У людей в переполнении стека есть более простое предложение?

Ответ на комментарии:

@Гленн Мейнард: Да, я отклонил ответ. Плохо писать пять строк, если есть способ сделать это в одном... особенно в случае, который появляется все время (цикл является общей чертой программ Turing-complete).

Для людей, которые ищут конкретный пример: скажем, я работаю с пользовательской библиотекой дат. Тогда мой вопрос будет, как бы вы выразили это в python:

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

Ответ 1

Это лучшее, что я могу придумать:

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"

Я хочу, чтобы у python был синтаксис закрытых выражений.

Изменить: Также обратите внимание, что вам нужно только определить cfor один раз (в отличие от вашей функции complicated_iterator).

Ответ 2

Я немного смущен: у вас сложное выражение while и сложное следующее выражение, но они прекрасно вписываются в цикл C? Это не имеет смысла для меня.

Я рекомендую пользовательский подход итератора. Вы, скорее всего, найдете другие варианты использования итератора, и инкапсуляция итерации в любом случае является хорошей практикой.

UPDATE: используя ваш пример, я бы определенно создал пользовательский итератор. Для меня совершенно естественно, что календарь может генерировать серию ежеквартальных дат:

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

Это кажется прекрасной абстракцией для вашего класса календаря, и я уверен, вы будете использовать его более одного раза.

Ответ 3

Как насчет:

date = start

while date < end:

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

    date = calendar.next_quarter_end(date)

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

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

Ответ 4

Вы можете использовать предложение try/finally для выполнения обновления:

val = START_VAL

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

    finally:
        val = <awkward/complicated update>

Caveat: это также выполнит оператор обновления, если вы выполните break.

Ответ 5

Я часто делаю

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

   etc.

Ответ 6

Хех:

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