Использование операторов break & continue

maxiim9

Наткнулся на обсуждение Python Coding Standards Spring 13 университета Мэрилэнд
http://www.csee.umbc.edu/courses/201/spring13/standards.shtm...
Так вот пункт break & continue не даёт мне покоя:
Using break and continue is not allowed in any of your code for this class. Using these statements damages the readability of your code. Readability is a quality necessary for easy code maintenance.
Это же не в силу каких-то особенностей питона, правда ведь?
Например, есть два массива condArr и valArr одинаковой длины LEN.
Ищем в массиве valArr индекс первого элемента, равного некоторой константе CONST, при неком условии на второй массив. Чем плох такой код и как его правильно написать, чтобы он отвечал современным стандартам?

...
for (i = 0; i < LEN; i++)
{
if (!check_cond (condArr[i]
continue;

if (valArr[i] == CONST)
break;
}

if (i < LEN)
printf ("We found i = %d\n", i);

...

Serab

Using break and continue is not allowed in any of your code for this class. Using these statements damages the readability of your code. Readability is a quality necessary for easy code maintenance.
Там похоже подбираются задания такие, что можно понятно и лаконично написать без break/continue. continue вообще не помню, когда последний раз пользовался.
Чем плох такой код и как его правильно написать, чтобы он отвечал современным стандартам?
Я бы написал как минимум так (зачем там continue неясно совершенно):
for (i = 0; i < LEN; i++)
{
if (check_cond (condArr[i]) && valArr[i] == CONST)
break;
}

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

Ivan8209

> Это же не в силу каких-то особенностей питона, правда ведь?
Нет, неправда. Это как раз в силу уродства питона в плане выбора синтаксиса.
Если бы "break" и "continue" требовали метку цикла, как это сделано в Аде,
то код был бы хотя бы минимально воспринимаемым, а без меток не всегда
понятно, откуда именно выходит "break" и что именно продолжает "continue".
---
...Я работаю...

Dasar

Чем плох такой код и как его правильно написать, чтобы он отвечал современным стандартам?
всякий continue

for (..)
if (условие)
code1;
continue;
code2;

можно переписать как:

for (..)
if (условие)
code1;
else
code2;

6yrop

а break?

Ivan8209

> if (check_cond (condArr[i]) && valArr[i] == CONST)
> break;
В сях, кстати, этот недостаток проявляется в полный рост,
так как "break" применяется не только к операторам циклов,
а ещё к ветвлению и составному оператору, но к последнему ---
"только по праздникам."
> (это, кстати, принятый в питоне способ выскакивать из двух вложенных
> циклов: рефакторинг выделение функции
И всё из-за того, что питон убог.
---
...Я работаю...

danilov


[i for i in range(min(len(valArr len(check_cond if valArr[i] == COND and check_cond (condArr[i])]

Без выхода на первом элементе, но наверное можно на итераторах сделать, язык не родной

6yrop

без меток не всегда
понятно, откуда именно выходит "break" и что именно продолжает "continue".
IDE же показывает

maxiim9

Я бы написал как минимум так (зачем там continue неясно совершенно):
Ну отчасти как раз для удобства восприятия
Кмк
if (!condFunc1(... continue; // my comment
if (!condFunc2(... continue; // my another comment
if (!condFunc3(... continue; // nice comment

воспринимается лучше чем

if (condFunc1(...) && condFunc2(...) && condFunc3(...) && testVar == CONST) {...}

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

Serab

Ну это, кстати, не аргумент. Во многих стайлгайдах для написания функций при наличии короткой ветки предлагается вместо

if( ... ) {
...
} else {
...
}

Писать

if( ... ) {
return ...;
}

...

Это упрощает чтение.
Та же идея может пройти с continue, если это в самом начале и ветка короткая.

Serab

как альтернатива:

if (condFunc1(...) // my comment
&& condFunc2(...) // my another comment
&& condFunc3(...) // nice comment
&& testVar == CONST) {...}

Но я согласен, иногда может быть нужно (я написал выше, когда это условие явно короче, чем остальной код).

6yrop

return после функции на случай, если не нашлось
Так?

tttt f
{
for (i = 0; i < LEN; i++)
{
if (check_cond (condArr[i]) && valArr[i] == CONST)
return i;
}
return xxx;
}

Что надо написать вместо ttt и xxx?

Dasar

а break?
через внешнюю переменную

for(условие)
{
if (условие1)
break;
code;
}

преобразуется в

var isContinue = true;
for(условие && isContinue)
{
if (условие1)
isContinue = false;
else
code;
}


ps
cложные преобразования начинаются, когда if с continue/break засунут во внутрь еще в несколько if-ов. Это потребует разворачивание if-ов

yroslavasako

как мне недавно показали, в питоне есть goto, и импорт его в рабочую область не сложен. Так что если запрещают break && continue - делай goto на метку, метка намного более наглядна

Serab

Ну, кстати, питон не может не одобрять break, потому что в нем есть известный for...else:


for i in range(LEN):
if check_cond(condArr[i]) and valArr[i] == CONST:
print "We found i = %d" % i
break
else:
print "Not found"

Ivan8209

> IDE же показывает
И оно правильно предупреждает тебя, что ты перенёс кусок кода,
где есть такой "break"?
---
...Я работаю...

Dasar

Во многих стайлгайдах для написания функций при наличии короткой ветки предлагается вместо
мне лично continue/break нравится. с ними код получается короче.
и с ходу мне сложно представить код, когда continue/break создают нечитаемость

Ivan8209

> Что надо написать вместо ttt и xxx?
ttt = int option
xxx = NONE
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

yroslavasako

и с ходу мне сложно представить код, когда continue/break создают нечитаемость
несколько вложенных циклом на пару экранов. Можно элементарно заблудиться

Dasar

несколько вложенных циклом на пару экранов. Можно элементарно заблудиться
в коде на несколько экранов легко заблудиться и без continue/break
имхо, скорее надо бороться с кодом на несколько экранов, чем с continue/break;
тем более, что IDE может показывать, какое continue/break какому циклу соответствует

BatoSan

несколько вложенных циклом на пару экранов. Можно элементарно заблудиться
Тут проблема не только в continue/break будет.

6yrop

И оно правильно предупреждает тебя, что ты перенёс кусок кода,
где есть такой "break"?
если для переноса используешь пункты меню "Рефакторинг", то, да, предупредит.
Если просто переносишь текст, то что если имена меток совпали?

Kira

Так что если запрещают break && continue - делай goto на метку, метка намного более наглядна

тэг [sarcasm] да?

Anturag

 for (i = 0; i < LEN; i++)
{
if (check_cond (condArr[i]) && valArr[i] == CONST)
break;
}

Для начала, кусок кода у топик-стартера и этот — это C, а не Python.
Далее, если уж так хочется избавиться от break (причины не рассматриваю то можно условие в цикл ввести, типа
  for (i = 0; i < LEN && valArr[i] != CONST; i++)
{
if (check_cond (condArr[i] {

}
}

evgen5555


(x for x in range(0, LEN) if condArr[x] and valArr[x] == CONST).next

читаемость конечно суперская :D
исключение еще придется ловить

Dmitriy82

Если break и continue damaging, то и return в любом месте кроме последней строчки функции - тоже.
Хотя вообще я допускаю, что break _до_ continue в теле одного и того же цикла могут создать помехи читателю, но такая ситуация должна встречаться редко.

bleyman

Вы все как будто не программисты (включая КОНТРУ, особенно включая причём!). Обсуждаете хуиту на простых примерах, например: ясен пень, если ты рассматриваешь выход из цикла с простым условием, то как нефиг его внести в инвариант цикла и получится лучше. В простых циклах не нужны break и continue, в них вообще дофига всего не нужно.
Как я понимаю вся эта муть поднялась после того, как Вирт сделал Паскаль и Дийкстра написал "goto considered harmful". Как это обычно бывает с религиями, нормальные люди нашли и аппроприировали полезное зерно, ненормальные продолжают аппелировать к избранным местам Писания, написанного в совсем другую эпоху.
Для начала, goto considered harmful было написано когда в бейсике if something goto label было _единственным_ возможным способом использования if statement. Предложение сделать if something begin ... end и использовать это как можно чаще было крайне уместным, немедленно пустило корни во всех языках программирования, и теперь люди которые впитали с молоком матери языки где так можно неправильно интерпретируют оригинальный посыл, который был что такая возможность должна быть и будет уместна почти всегда, а как то, что надо всегда делать так. Потому что читают этот текст как если бы эта возможность уже есть, и следовательно он призывает неиспользовать goto не вместо структурного if, а вообще.
Далее, где-то там между Дийкстрой и Виртом была идея Структурного Программирования, которую они, заценив как заебически оно работает для большинства случаев, попытались продвинуть до full retarded, типа, чтобы любой кусок кода ограниченный begin...end можно было бы скопипейстить в любое другое место. Это, блджад, не работает и не может работать (как минимум потому что локальные переменные и у нас есть гораздо лучшие способы делать reusable code, выделяя его в referentially transparent функции например, где все зависимости должны быть явными. Типа, это была клёвая идея но оказалось что она не работает и есть другие идеи которые работают и гораздо лучше.
Но нет, люди опять и опять находят старые посты Дийкстры и Вирта, вменяемые идеи из которых уже есть во всех языках программирования, и смотрят на идеи которые время продемонстрировало как невменяемые (но это естественно не отражено в оригинальных постах и пытаются их использовать потому что блджад Авторитет. Или они услышали через третьи руки что нельзя использовать goto, break, continue, return.

Werdna

Да, я тут вот подпишусь под каждым словом. Нелюбовь к continue/break и тем более к goto — это вирус в мозге.
Уродливы не сами операторы. Уродливо их используют. И даже если запретить goto/break/continue, всё равно быдлокодер найдёт способ написать плохо.
При этом талантливый разработчик напишет понятно и с goto.

Dasar

приведи, пожалуйста, примеры красивого кода с goto, которые не сводятся к return/break/continue с меткой цикла.

Anturag

Ядро Linux — это красивый код (тут считает до трёх и успокаивается я уже считал , что в среднем 5 штук goto на файл сорца в Linux, безотносительно «сведе́ния» о котором ты говоришь, так как оно, возможно, испортит прекрасный код, так что читай сорцы ядра.

beluchy

приведи, пожалуйста, примеры красивого кода с goto, которые не сводятся к return/break/continue с меткой цикла.
иногда это короче и читается лучше :p

while (I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS){
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SUCCESS) goto end;
};
...
end:

vs

FlagStatus af=RESET;

while (I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS && af == RESET){
af = I2C_GetFlagStatus(I2C1, I2C_FLAG_AF);
};
if (af == RESET){
...
}

vall

приведи, пожалуйста, примеры красивого кода с goto
обработка ошибок и откат предыдущих действий
err = do_foo;
if (err)
 goto err_foo;
err = do_bar;
if (err)
 goto err_bar;
err = do_baz;
if (err)
 goto err_baz;
erturn 0;
err_baz:
undo_bar;
err_bar:
undo_foo;
err_foo:
return err;

apl13

приведи, пожалуйста, примеры красивого кода с goto, которые не сводятся к машине Тьюринга

apl13

Да вообще, наверное, любые автоматические действия при выходе из видимости.
В сях нет нетривиальных деструкторов, не говоря уже о dynamic-wind.
Поэтому если у тебя в начале функции открывается файл, а из нее десять разных точек выхода, логично вместо return там написать goto close_file_and_return;.
(Допустим на секунду, что десять разных точек выхода это хорошо.)

Ivan8209

> В сях нет нетривиальных деструкторов, не говоря уже о dynamic-wind.
Использование объектов в качестве замены with-something-done
это тот ещё прикол.
> Поэтому если у тебя в начале функции открывается файл, а из нее
> десять разных точек выхода
И здесь мы опять возвращаемся к Дейкстре, Вирту и оправданию
использования "goto" через отсутствие вменяемых конструктов.
---
"А я обучался азбуке с вывесок,
листая страницы железа и жести."

Dasar

обработка ошибок и откат предыдущих действий
следующий код, имхо, понятнее выглядит - в явном виде имея записанный паттерн transaction. (далее псевдокод).

transaction_context trans;
trans=>do_foo =>undo_foo;
trans=>do_bar =>undo_bar;
do_baz;
trans.commit;

Dasar

в C-ишном коде goto действительно необходим из-за того, что в нем нехватка качественных идиом.
но в C++, c#/java, python уже достаточно высокоуровневых идиом, которые делают код понятнее, чем goto

6yrop

Вопрос: компилятор отловит вот такую ошибку?
err = do_foo;
if (err)
 goto err_foo;
do_bar;
if (err)
 goto err_bar;
err = do_baz;
if (err)
 goto err_baz;
erturn 0;
err_baz:
undo_bar;
err_bar:
undo_foo;
err_foo:
return err;
При структурном программировании отловит, у тебя нет.

6yrop

далее псевдокод
пиши на существующем языке, а то ты потом начнешь правила языка додумывать

Ivan8209

> но в C++, c#/java, python уже достаточно высокоуровневых идиом,
> которые делают код понятнее, чем goto
Ни в плюсах, ни в яве, ни в питоне устранение хвостовых вызовов не то,
чтобы не гарантируется, оно принципиально не делается. Из-за этого
принципиально невозможно реализовывать автоматы вменяемым способом.
В питоне, кстати, это проявляется сильнее всего, так как сама
реализация медленная.
---
"Мы диалектику учили не по Гегелю.
Бряцанием боёв она врывалась в стих..."

Dasar

правильнее договориться о наличии идиом в языке.
в данном случае использовались идиомы:
- ошибка, как исключение
- очистка ресурсов по выходу из блока
- лямбда выражение
это всё есть в c# 3.5 и c++ 11. в python-е и в java-е не помню есть ли идиома "очистка ресурсов по выходу из блока"

Dasar

Ни в плюсах, ни в яве, ни в питоне устранение хвостовых вызовов
я до сих пор считаю, что сведение к рекурсии более худшая идиома (в плане предсказуемости чем идиома конвейерной обработки последовательностей и деревьев.
соответственно, отсутствие устранения хвостовых вызовов - не критично.

yroslavasako

ни в яве
ты гонишь. в яве хвостовая рекурсия конвертится в цикл, так написано, и я сам проверил

Ivan8209

> в яве хвостовая рекурсия конвертится в цикл
Иди подучи русский язык, может быть, узнаешь, чем вызов
отличается от рекурсии.
> я сам проверил

int f(int n) {if (n != 0) return g(n-1); else return 0;};
int g(int n) {if (n != 0) return f(n-1); else return 0;};

Иди, проверяй.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

yroslavasako

окей, поясни чем отличается.
И да, я проверял основательно, на разных примерах и твой для меня не новость. Оптимизация хвостовой рекурсии явы в теории и на практике работает только для прямой рекурсии, то есть осуществляемой в пределах одного метода. В теории ещё долго будет работать именно так, потому как более общая оптимизация несовместима с механизмом безопасности jvm

Ivan8209

> я до сих пор считаю, что сведение к рекурсии более худшая
> идиома (в плане предсказуемости чем идиома конвейерной
> обработки последовательностей и деревьев.
Да ты много чего считаешь, мы это уже давно знаем.
> соответственно, отсутствие устранения хвостовых вызовов - не критично.
Когда человек под автоматизацией производства понимает перекладывание бумажек,
то ему и устранение хвостовых вызовов не нужно.
---
"Дебилы, несмотря на замедленность и конкретность мышления,
низкий уровень суждений, узкий кругозор, бедный запас слов
и слабую память, способны к приобретению некоторых знаний
и профессиональных навыков."

Dasar

зачем тебе понадобилась рекурсия для управления шаговым двигателем?

6yrop

невозможно реализовывать автоматы вменяемым способом

с примером кода было бы лучше

Ivan8209

> окей, поясни чем отличается.
Тем и отличается.
> И да, я проверял основательно, на разных примерах и твой для
> меня не новость. Оптимизация хвостовой рекурсии явы в теории
> и на практике работает только для прямой рекурсии, то есть
> осуществляемой в пределах одного метода. В теории ещё долго
> будет работать именно так, потому как более общая оптимизация
> несовместима с механизмом безопасности jvm
Не знаю насчёт "механизма безопасности," а на практике
отсутствие такой банальной оптимизации (которая совсем не
оптимизация) приводит к тому, что какие-нибудь тупые конечные
автоматы (с памятью или без) приходится переписывать через
специально придуманные обходные идиомы, приводящие к медленнее
работающему коду, так как на каждом шаге создаётся и уничтожается
куча стековых фреймов. В питоне, например, накладные расходы
могут быть вообще заоблачными, наподобие 30% времени, как у нас
получалось на одном прототипе.
---
"Мы диалектику учили не по Гегелю.
Бряцанием боёв она врывалась в стих..."

vall

Вопрос: компилятор отловит вот такую ошибку?
зависит от компилятора и опций. sparse отловит.

6yrop

зависит от компилятора и опций. sparse отловит.
И какую ошибку выдаст компилятор?
Дело в том, что код правильный, он просто делает другое. Компилятор не может запретить делать другое. :)

Ivan8209

> Дело в том, что код правильный, он просто делает другое.
Насколько имеет смысл объяснять, зачем писать "(void) snprintf(...)",
человеку, опирающемуся на средства, которые выполняют преобразования кода,
обоснования корректности которых неизвестны?
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

6yrop

ты, наверное, пошутил, но я нихера не понял. Твой батхерт по поводу использования современных инструментов, да, чувствуется.

zya369

шутят другие
а контра доказывает, сурово доказывает
(с)

6yrop

как обычно, подпёздываешь?

yroslavasako

Функции песочницы явы явно важнее такой элементарной оптимизации, как хвостовая рекурсия. Потому как в наборе микробенчмарков победил тот, в котором алгоритм был переписан в императивном стиле. Просто и банально таблица состояний. Так что без оптмизиации хвостовой рекурсии жить можно и хорошо

apl13

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

Ivan8209

> Функции песочницы явы явно важнее
Можешь привести ссылку, какие именно функции имеются в виду?
Я ни разу пока ещё не встречал случая, когда какие-то мифические
функции оказывались важнее таких элементарных оптимизаций,
как развёртывание тела функции и устранение хвостового вызова,
а вот обратное, когда из-за идиотского ограничения языка
приходилось переписывать код в совершенно нечитаемые таблицы
переходов, встречал не раз.
---
"Прогресс науки обратно пропорционален числу выходящих журналов."

yroslavasako

Можешь привести ссылку, какие именно функции имеются в виду?
http://www.javaworld.com/javaworld/jw-05-1997/jw-05-security...

bleyman


приведи, пожалуйста, примеры красивого кода с goto, которые не сводятся к return/break/continue с меткой цикла.
Реальные примеры влом искать, но дофига случаев когда если нужно сделать три или более if ...: continue, которые иначе превращались бы во вложенность утыкающуюся в правый край экрана. Мне по нраву плоский код. При этом вроде без понта пытаться их на функции разносить, потому что логически одна хрень, и проще её понять читая подряд, а не прыгая к определениям функций которым и вменяемые имена-то не дашь.
Алсо, я не верю в наивную парадигму структурного программирования что типа если б там этих continue/break/return не было, то ты бы всё сразу понял посмотрев на заголовок цикла, не читая его тело. Щаз.

transaction_context trans;
trans=>do_foo =>undo_foo;
trans=>do_bar =>undo_bar;
do_baz;
trans.commit;

То есть типа do_foo обломалось но мы всё равно делаем do_bar и do_baz? Замечательно. Ещё чуть-чуть и ты придумаешь как это завернуть в монаду!

Dasar

То есть типа do_foo обломалось но мы всё равно делаем do_bar и do_baz? Замечательно. Ещё чуть-чуть и ты придумаешь как это завернуть в монаду!
перед монадами тебе стоит вспомнить про исключения

bleyman

Охохо, то есть у тебя transaction_context вместо того, чтобы быть использованным с with и отвечать за commit/rollback, ещё и ловит эксепшены от передаваемых функций, если поймалось то выполняет роллбэк функцию (только эту, или все предыдущие? Дико неявно их запоминая? потом кидает эксепшен опять, потом сам же его ловит и делает rollback, потом кидает его опять.
Чота как-то дофига магеи, не?

Dasar

как-то у тебя всё сложно...
transaction_context отвечает за хранение и добавление rollback-функций, если успешно выполнилась основная функция, и откат всех предыдущих ролбаков, если выполнилась не успешно
псевдокод TransactionContext следующий:

class TransactionContext
{
List<Action> rollbacks = new List<Action>
public void operator (Action do, Action undo)
{
try
{
do;
rollbacks.Add(undo);
}
catch
{
UndoAll;
throw;
}
}
public void commit
{
rollbacks.Clear;
}

public ~TranscationContext
{
UndoAll;
}
public void UndoAll
{
foreach (var undo in rollbacks.Reverse
undo;
rollbacks.Clear;
}
}

bleyman

а на практикеотсутствие такой банальной оптимизации (которая совсем неоптимизация) приводит к тому, что какие-нибудь тупые конечныеавтоматы (с памятью или без) приходится переписывать черезспециально придуманные обходные идиомы, приводящие к медленнееработающему коду, так как на каждом шаге создаётся и уничтожаетсякуча стековых фреймов. В питоне, например, накладные расходымогут быть вообще заоблачными, наподобие 30% времени, как у насполучалось на одном прототипе.
Это потому, что ты плохой программист, бро!
Описание конечного автомата должно быть декларативным, реифицированным как бы. Чтобы потом ты мог например мог пройтись по нему и посмотреть все ли возможные случаи описаны, а то и соптимизить его как-нибудь. И только потом ты его должен компилить в исполняемый код. Описание — отдельно, компиляция — отдельно. Помимо прочего отдельная компиляция позволит тебе скомпилить его дико эффективно, хоть прямо в питоновский байткод, хоть в С.
Это, кстати, дико яркий пример лиспа головного мозга, между прочим! Помнится, когда я зырил на курс Норвига про дизайн программ на Udacity меня именно это дико напрягло: вот он такой, зацените как клёво можно наш парсер перехуячить чтобы каждая штука возвращала лямбду, и даже вфигачить какую-то незначительную оптимизацию в процессе, что-то там шарится между разными лямбдами!
Не надо так делать, бро. Парсинг/декларации и компиляция должны быть отдельными шагами. Организовывать змеиную свадьбу из нереифицированной компиляции понатыканной промеж парсинга (и потом жаловаться что всё тормозит на Insufficiently Clever Evaluator) это как бы тупик прогресса, и вообще тупик и тупняк. То, что ты так можешь, не означает, что так нужно делать.
@: ну да, я и говорю, какая-то стрёмная неочевидная магея, мне дико не по нраву. Ты это уже использовал в продакшене? И коворкеры тебя не отпиздили за это?

Dasar

@: ну да, я и говорю, какая-то стрёмная неочевидная магея, мне дико не по нраву. Ты это уже использовал в продакшене? И коворкеры тебя не отпиздили за это?
и где магия?
это же просто стандартная реализация стандартного паттерна распределенной транзакции. и именно она и была в исходном примере.
ps
а ты как бы написал тоже самое?

bleyman

Магея в том, что по виду кода совершенно непонятно что реверт-коллбэки где-то сохраняются.
Стрёмность магеи в том, что если ты между вызовами что-то сделаешь, то оно не отревертится.
То есть я бы понял если бы это всё выглядело как
execute_transactionally(params ActionAndRollback[] actions)
, ну, сразу видно что функция может делать что-то нетривиальное, и довольно тяжело напортачить, чисто синтаксически. А у тебя оно как бы делает вид что ты можешь делать всё, что позволяется языком, между вызовами твоей штуки. А на самом деле — нет!

Dasar

это уже вкусовщина на тему, что лучше: иметь краткий код или код с функциями вида: ЭтоНадоВызыватьОченьАккуратно, НеПытайтесьЭтоПовторитьЕслиВыНеПрофессионал, НеСушитеЖивотныхВМикроволновке и т.д.
ps
опыт показывает, что все эти предупреждающие названия болезнь переходного периода, когда разработчик еще не освоился с мощным инструментом и хочет всё вокруг обвесить предупреждающими флажками

bleyman

Ты, по-моему, нифига не понял что я сказал.
Суть не в том, что функция называется execute_transactionally, а в том, что тебе будет тяжело незаметно для себя вставить туда нетранзакционированные действия. Потому что она получает массив action/rollback. И сразу видно что всё это является своего рода DSL.
Твой же код выглядит как обычный C#, и "освоение с мощным инструментом" состоит в том, что разработчик должен откуда-то узнать, что если он попытается сделать что-нибудь обычно-сишарповое между вызовами, типа вместо твоего

trans=>do_foo =>undo_foo;
trans=>do_bar =>undo_bar;
do_baz;

написать

trans=>do_foo =>undo_foo;
do_baz;
trans=>do_bar =>undo_bar;

то всё скомпилится отлично, но он получит неотлавливаемый баг в рантайме (потому что обычно-то транзакция не роллбэкается, а баг будет только тогда, когда случится ошибка в дико конкретном месте).
Я бы такие грабли перед собой не расстилал. И с немудрыми людьми которые считают себя достаточно умными чтобы их обходить, не работал бы.
edit: и кстати мой вариант-то более кратким получится, если что. По крайней мере пока все действия независимы, а когда они становятся зависимыми — нафигачь монаду же, благо даже няшный синтаксис для них в языке есть.

Dasar

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

if (условие)
trans=>do_foo =>undo_foo;
for(var i = 0; i < 10; ++i)
trans=>do_bar(i =>undo_bar(i;
do_baz;

и переписывание его через предложенную тобой идиому его лучше не делает

var actions = new List<TransactionAction>
if (условие)
actions.Add=>do_foo =>undo_foo;
for(var i = 0; i < 10; ++i)
actions.Add=>do_bar(i =>undo_bar(i;
actions.Add=>do_baz =>{});

execute_transactionally(actions);

что самое интересное твоя идиома никак не спасает от всё той же самой ошибки

execute_transactionally
(
new taction=>do_foo =>undo_foo
new taction=>{do_baz;do_foo}, =>undo_foo
)

do_baz здесь ровно так же не откатывается. и нафига тогда такое изменение?

Ivan8209

> Реальные примеры влом искать,
А ты всё-таки найди.
> но дофига случаев когда если нужно сделать три или более
> if ...: continue, которые иначе превращались бы во вложенность
> утыкающуюся в правый край экрана.
И я расскажу тебе, как писать так, чтобы не утыкаться в правый край.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

Ivan8209

> Описание конечного автомата должно быть декларативным,
> реифицированным как бы.
Реифицированным как что?
У хороших программистов автомат уже реифицирован как набор функций
по одной на состояние.
> Чтобы потом ты мог например мог пройтись по нему и посмотреть
> все ли возможные случаи описаны,
Если у тебя каждому состоянию соответствует функция,
то пропустить её определение довольно сложно.
> а то и соптимизить его как-нибудь.
Соптимизировать что? Удалить недостижимые состояния?
Свернуть цепочку переходов в один даже туповатый компилятор может сделать,
правда, не в питоне, да.
> И только потом ты его должен компилить в исполняемый код.
Опыт показывает, что это на такое неоправданное усложнение жизни
приходится только из-за того, что существуют такие туповатые языки,
как питон и ява.
> Парсинг/декларации и компиляция должны быть отдельными шагами.
Осталось убедить коллег, что они должны использовать генератор
конечных автоматов (и потом прикручивать к нему память, а то и стек
а не изобретать странное.
> и потом жаловаться что всё тормозит на Insufficiently Clever Evaluator
Устранение хвостового вызова это настолько тупая оптимизация,
что она не выполняется только какими-то очень уж тупыми оптимизаторами.
Кстати, а если генератор КА туповат, то на что жаловаться?
---
"Мы диалектику учили не по Гегелю.
Бряцанием боёв она врывалась в стих..."

6yrop

лиспа головного мозга
....
обычно-сишарповое ....
у тебя кажись DSL головного мозга.
Нет такого понятия "обычно-сишарповое".

6yrop

использовать генератор
конечных автоматов
я ж говорю у человека DSL головного мозга, свой язычок на каждый случай

Ivan8209

> я ж говорю у человека DSL головного мозга, свой язычок на каждый случай
Иметь DSL это не плохо, проблема совершенно в другом:
очень часто все эти языки неоправданно ограничены
и несовместимы друг с другом.
Например, потребности растут, и внезапно из автомата Мили,
который уже не автомат Мили, а просто что-то очень похожее,
надо сделать что-то ещё сложнее.
---
"Вот тут как раз и начинается кино,
И подливает в это блюдо остроты
Белова Танечка, глядящая в окно,---
Внутрирайонный гений чистой красоты."

6yrop

очень часто все эти языки неоправданно ограничены
и несовместимы друг с другом
именно про это я и говорю.
Иметь ограниченные, несовместимые DSL-и не плохо?

Werdna

А ты всё-таки найди.
Я тебе приведу код очень быстро сишный.
 
 

int do_spherical_action_in_space(int a, int b)
{
void* p = malloc(a);
if (!p) return -1;

if (ERR1) goto ret;

if (ERR2) goto ret;

if (ERR3) goto ret;

...

return XXX;

ret:
free(p);
...что-то ещё...
return -1;
}


Собственно, в плюсах это бы хорошо заменилось наличием деструктора у переменной, а вот в сях тебе надо все ресурсы освободить аккуратно, если что-то пошло не так.
Так как условий много, то даже банально один free (плюс скобочки {}) дадут увеличение числа строк.
Короче, читай код хорошо написанных штук, а не высказывания из интернетов, вырванные из контекста.

6yrop

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

Werdna

а есть пример, который не связан с недостатками конкретного языка?
Т.е. хочется пример, который ни на одном из существующих языков нельзя хорошо переписать без goto.
Это не недостаток языка! Более того, ЛЮБУЮ конструкцию с goto можно переписать и сделать без goto.
Непонятно другое — откуда ненависть к goto? Си — это ассемблер, и операторы группы JMP — нормальное явление.
Пойми, что проблема не в goto. Проблема в том, что чтобы его умело использовать, надо 3-4 года опыта, чтобы уже языком в совершенстве владеть.

6yrop

Ок, я готов заменить слово "недостатками" на "особенностями". Можешь на тот вопрос ответить после такой правки?

6yrop

это ассемблер
у структурного программирования и у ассемблера разные цели. Зачем их противопоставлять? :confused:

Werdna

Можешь на тот вопрос ответить после такой правки?
Любой код можно записать только используя if один. :)
Вообще, в тех же плюсах всякие деструкторы появились именно чтобы этот целый пласт случаев необходимости goto убрать.
Если ты заметил, goto используется в сишном коде и почти никогда в плюсовом.

agaaaa

int do_spherical_action_in_space(void* p, int b) {
if (!p) return -1;
if (ERR1) return -1;
if (ERR2) return -1;
if (ERR3) return -1;
... return XXX;
}
void* p = malloc(a);
int r = do_spherical_action_in_space(p);
free(p);
Кэп

Werdna

ты же понимаешь, что я синтетический пример привел.
Возьми несклько действий: проверить наличие файла, узнать размер, что-то хитрое выделить, потом посчитать, потом опять новое уже выделить, ещё чо-то сделать. И на любом этапе всё завернуть с ошибкой.

agaaaa

Возьми несклько действий: проверить наличие файла, узнать размер, что-то хитрое выделить, потом посчитать, потом опять новое уже выделить, ещё чо-то сделать. И на любом этапе всё завернуть с ошибкой.
Описываешь типичный Try-Catch-Finally, который на if-ах будет выглядеть лучше goto хотябы из-за того, что области жизни ресурсов будут показаны отступами во внутренних блоках.

Werdna

Описываешь типичный Try-Catch-Finally, который на if-ах будет выглядеть лучше goto хотябы из-за того, что области жизни ресурсов будут показаны отступами во внутренних блоках.
Ну try/catch и есть такая вот замена goto ;) (что за finally? мы о плюсах же)
Не забываем только, что try/catch — это дорогое удовольствие. заменять хороший быстрый код с goto на исключения — это терять производительность. для узкого места, где обломы происходят стабильно — хреновая идея.

apl13

если поймалось то выполняет роллбэк функцию (только эту, или все предыдущие? Дико неявно их запоминая?)
Почему неявно-то? Можно же функцию и кидать.

vall

И какую ошибку выдаст компилятор?
Дело в том, что код правильный, он просто делает другое. Компилятор не может запретить делать другое.
код неправильный. совсем.
В ядре на функции лепят атрибут __must_check (warn_unused_result) и ловят -Wunused-result

vall

следующий код, имхо, понятнее выглядит - в явном виде имея записанный паттерн transaction. (далее псевдокод).
кому как. на таком тривиальном примере возможно и понятнее. но всё равно уродливо.
лучше уж налепить объекты реализующие операции транзакции чем городить это уродство.
другой метод это реализовывать 'undo' операции безопасными на случай если 'do' не был позван.
типа free который прекрасно работает для NULL. тогда на unwind можно звать полноценный деструктор.

6yrop

В ядре на функции лепят атрибут __must_check (warn_unused_result) и ловят -Wunused-result
я тут еще раз посмотрел на твой код. Моя претензия была связана с использование mutable переменной err. Но твой код с goto можно переписать и без mutable переменной:
 
 
int err_result;
{
var err = do_foo;
if (err) {
err_result = err;
goto err_foo;
}
}

{
var err = do_bar;
if (err) {
err_result = err;
goto err_bar;
}
}

{
var err = do_baz;
if (err) {
err_result = err;
goto err_baz;
}
}

return 0;

err_baz:
undo_bar;
err_bar:
undo_foo;
err_foo:
return err_result;

В таком виде goto вполне хорош. Спасибо за хороший пример c goto.

6yrop

Кстати, без goto прям так сильно хуже?
 
bool Method1
{
{
var err = do_foo;
if (err) {
err_foo;
return err;
}
}

{
var err = do_bar;
if (err) {
err_bar;
return err;
}
}

{
var err = do_baz;
if (err) {
err_baz;
return err;
}
}

return false;
}

void err_baz
{
undo_bar;
err_bar;
}

void err_bar
{
undo_foo;
err_foo;
}

void err_foo
{
//no-op
}

Ventalf

Using break and continue is not allowed in any of your code for this class. Using these statements damages the readability of your code. Readability is a quality necessary for easy code maintenance.
Херня какая то. Не обращай внимание. Сколько людей столько и предпочтении. Для меня лично принцып break, continue вполне читабелен. В Google Python coding standard подобного ограничения нету.

Ventalf

Чем плох такой код и как его правильно написать, чтобы он отвечал современным стандартам?
это явно не питоношный код
Главное выучи Google Python Coding Standard и используй утилиту pylint.py.
Первое это всегобщепризнанный стандарт а второе это соответсвующяя утилита для проверки соответствующего стиля, и поиска ошибок.
Оставить комментарий
Имя или ник:
Комментарий: