Имитация стиля 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...
Ответы
Ответ 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"
)