data = [5, 7, 10, 2, 7, 8, 1, 3,2]
def func(data):
result =[]
temp =[]
data.append(data[-1])
for i in range(1,len(data)):
if data[i]>=data[i-1]:
temp.append(data[i-1])
else:
temp.append(data[i-1])
temp.reverse()
result.extend(temp)
temp=[]
if len(temp)!=0:
temp.reverse()
result.extend(temp)
temp.clear()
return result
print(func(data))
# output [10, 7, 5, 8, 7, 2, 3, 1, 2]
FWIF, I find the error handling idiom you gave in the question's example to be more readable and easier to understand than any of the alternatives given in the answers so far. While goto
is a bad idea in general, it can be useful for error handling when done in a simple and uniform manner. In this situation, even though it's a goto
, it's being used in well-defined and more or less structured manner.
Мне кажется, что cleanup_3
должен выполнить его очистку, а затем вызвать cleanup_2
. Аналогично, cleanup_2
должен выполнить очистку, а затем вызвать cleanup_1. Похоже, что каждый раз, когда вы делаете cleanup_ [n]
, что cleanup_ [n-1]
требуется, таким образом, это должно быть ответственностью метода (так что, например, cleanup_3
никогда нельзя вызвать, не вызвав cleanup_2
и, возможно, вызвав утечку.)
Учитывая такой подход, вместо gotos, вы просто вызовете процедуру очистки, а затем вернетесь.
Подход goto
не является неправильным или плохим , однако, просто стоит отметить, что это не обязательно «самый чистый» подход (ИМХО).
Если вы ищете оптимальную производительность, то я полагаю, что решение goto
является наилучшим. Однако я ожидаю, что это будет уместно только в некоторых приложениях, критичных к производительности (например, драйверы устройств, встроенные устройства и т. Д.). В противном случае это микрооптимизация, которая имеет более низкий приоритет, чем четкость кода.
Я лично являюсь последователем «Сила десяти - 10 правил написания критического кода безопасности» .
Я включу небольшой фрагмент этого текста это иллюстрирует то, что я считаю хорошей идеей о goto.
Правило: ограничивайте весь код очень простыми конструкциями потока управления - не используйте goto операторы, конструкции setjmp или longjmp, а также прямая или косвенная рекурсия.
Обоснование: упрощение потока управления приводит к расширению возможностей проверки и часто приводит к повышению четкости кода. Возможно, изгнание рекурсии Самый большой сюрприз здесь. Без рекурсии, тем не менее, мы гарантируем граф вызовов ациклических функций, который может быть использован анализаторами кода и может прямо помогите доказать, что все казни, которые должны быть ограничены, на самом деле ограничены. (Обратите внимание, что это правило не требует, чтобы все функции имели одну точку возврата - хотя это часто также упрощает управление потоком. Есть достаточно случаев, хотя, где раннее возвращение ошибки - более простое решение.)
Изгнать использование goto кажется плохим , но:
Если правила кажутся Сначала драконовцы, имейте в виду, что они предназначены для проверки кода где буквально ваша жизнь может зависеть от ее правильности: код, который используется для контроля самолет, на котором вы летите, атомная электростанция в нескольких милях от того места, где вы живете, или космический корабль, который доставляет астронавтов на орбиту. Правила действуют как ремень безопасности в вашей машине: первоначально они, возможно, немного неудобны, но через некоторое время их использование становится вторая натура и неиспользование их становится невообразимым.
GOTO полезен. Это то, что может сделать ваш процессор, и именно поэтому у вас должен быть к нему доступ.
Иногда вы хотите добавить что-то в свою функцию, и единый переход позволяет вам сделать это легко. Это может сэкономить время ..
В выражении goto нет ничего морально неправильного, кроме как что-то морально неправильное с (void) * указателями.
Все дело в том, как вы используете инструмент. В представленном вами (тривиальном) случае оператор case может реализовать ту же логику, хотя и с большими накладными расходами. На самом деле вопрос в том, каково мое требование к скорости?
goto довольно быстр, особенно если вы тщательно следите за тем, чтобы оно компилировалось в короткий прыжок. Идеально подходит для приложений, где скорость является премией. Для других приложений, вероятно, имеет смысл использовать накладные расходы в случае поддержки if / else +.
Помните: goto не убивает приложения, разработчики убивают приложения.
ОБНОВЛЕНИЕ: Вот пример случая
int foo(int bar) {
int return_value = 0 ;
int failure_value = 0 ;
if (!do_something(bar)) {
failure_value = 1;
} else if (!init_stuff(bar)) {
failure_value = 2;
} else if (prepare_stuff(bar)) {
return_value = do_the_thing(bar);
cleanup_3();
}
switch (failure_value) {
case 2: cleanup_2();
case 1: cleanup_1();
default: break ;
}
}
Проблема с ключевым словом goto
в основном неправильно понята. Это не просто зло. Вам просто нужно знать о дополнительных путях управления, которые вы создаете при каждом переходе. Становится трудно рассуждать о вашем коде и, следовательно, о его достоверности.
FWIW, если вы посмотрите учебники developer.apple.com, они используют подход goto к обработке ошибок.
Мы не используем gotos. Более важное значение уделяется возвращаемым значениям. Обработка исключений осуществляется с помощью setjmp / longjmp
- всего, что вы можете.
Как правило, хорошая идея - избегать goto, но злоупотребления, которые были распространены, когда Дейкстра впервые написал «GOTO рассмотрено как вредное», в наши дни даже не приходят в голову большинству людей как вариант. .
То, что вы обрисовали в общих чертах, является обобщенным решением проблемы обработки ошибок - мне это подходит, если его тщательно использовать.
Ваш конкретный пример можно упростить следующим образом (шаг 1):
int foo(int bar)
{
int return_value = 0;
if (!do_something(bar)) {
goto error_1;
}
if (!init_stuff(bar)) {
goto error_2;
}
if (prepare_stuff(bar))
{
return_value = do_the_thing(bar);
cleanup_3();
}
error_2:
cleanup_2();
error_1:
cleanup_1();
return return_value;
}
Продолжение процесс:
int foo(int bar)
{
int return_value = 0;
if (do_something(bar))
{
if (init_stuff(bar))
{
if (prepare_stuff(bar))
{
return_value = do_the_thing(bar);
cleanup_3();
}
cleanup_2();
}
cleanup_1();
}
return return_value;
}
Это, я считаю, эквивалентно исходному коду. Это выглядит особенно чисто, так как исходный код сам по себе был очень чистым и хорошо организованным. Часто фрагменты кода не так аккуратны, как это (хотя я бы согласился с аргументом, что они должны быть); например, часто есть больше состояний, чтобы передать процедурам инициализации (настройки), чем показано,
Я думаю, что вопрос здесь ошибочен относительно данного кода.
Учтите:
Следовательно: do_something (), init_stuff () и prepare_stuff () должны выполнять свою собственную очистку . Наличие отдельной функции cleanup_1 (), которая очищает после do_something (), нарушает философию инкапсуляции. Плохой дизайн.
Если они производили очистку самостоятельно, то foo () становится довольно простым.
С другой стороны. Если foo () действительно создает свое собственное состояние, которое необходимо удалить, тогда подойдет goto.