Форум: "Прочее";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];
ВнизЧто большее зло: goto или while true do ? Найти похожие ветки
← →
GrayFace © (2010-05-13 08:35) [40]И то, и другое - добро.
Mystic © (12.05.10 11:10) [13]
Ну а goto в умеренных количествах использовать можно. Если имеется нетривиальная алгоритмическая логика, то вряд ли использование кучи флагов для организации нужного goto вряд ли делает код понятнее :)
Слабо представляю такую логику.
Mystic © (12.05.10 18:16) [33]
Лучшеconst
True = False;
repeat
until True;
while true do читается гораздо проще, чем двойное отрицание в until false.
← →
Mystic © (2010-05-13 20:34) [41]
> Petr V. Abramov © (12.05.10 22:53) [39]
Ну exit when в Ada выходит не только из циклов, но и из блоков begin..end. Т. е. можно так:
declare
I: Integer;
begin
-- Do something
exit when I < 0;
end;
Также допустимы именованные блоки и циклы, так что можно писать
Something_Difficult:
declare
-- variables
begin
Inner_Loop:
loop
-- Calculations
exit Inner_Loop when I > 0;
exit Something_Difficult when I = 0;
exit when I < 0; -- выходим ил цикла
end loop Inner_Loop;
end Something_Difficult;
← →
Mystic © (2010-05-13 20:36) [42]
> Слабо представляю такую логику.
Любой конечный автомат.
← →
Mystic © (2010-05-13 20:45) [43]Опять же, что-нить вроде такого
repeat
{
А тут море всяких вложенных циклов и прочей радости,
но у любой момент возможно либо принятие Current,
либо мы можем определить, что он нам не подходит.
Соответственно goto FindOk или goto Next выглядят
вполне нормально
}
FindOk:
List.Add(Current);
Next:
Current := GetNextExelemt(Current);
until Current = nil;
Альтернативный вариант такой, но я не могу сказать, что он такой уж легко читаемый + производительность :)
repeat
try
try
{
А тут море всяких вложенных циклов и прочей радости
}
except
on E: EAccept do ;
end;
List.Add(Current);
except
on E: EReject do;
end;
Current := GetNextExelemt(Current);
until Current = nil;
← →
RWolf © (2010-05-14 00:55) [44]
> Mystic © (13.05.10 20:36) [42]
> Любой конечный автомат.
>
Конечные автоматы, кроме самых простых, лучше описывать в терминах предметной области — набор состояний, таблица переходов, — чем городить лес из goto. Так и понятнее, и багов меньше.
Простые автоматы прекрасно моделируются оператором case State of …, опять же, в goto необходимости нет.
← →
RWolf © (2010-05-14 01:02) [45]чтобы не быть голословным — на днях попался на глаза примерчик: http://rybalko.nm.ru/html/soft/dfmconv.dhtml
см. pars.pas
← →
Игорь Шевченко © (2010-05-14 01:03) [46]RWolf © (14.05.10 00:55) [44]
Не всегда и все так просто, как кажется.
← →
Mystic © (2010-05-14 01:36) [47]
> Простые автоматы прекрасно моделируются оператором case
> State of …, опять же, в goto необходимости нет.
Конечно нет. Вполне можно написать так:
while (state != FINAL)
switch(state)
{
case CASE_1:
if (something)
{
state = CASE_3;
break;
}
state := CASE_2;
break;
// .........
}
Вот только чем это лучше простого???
CASE_1:
if (something) goto CASE_3;
goto CASE_2;
...
FINAL:
...
Как по мне разница только в том, что в первом случае нет goto, а второй вариант лаконичнее.
Фокус в том, что любую программу с использование goto можно переписать без использования goto. Но встречаются случаи, когда потом мысленно, для того, чтобы разобраться, все равно надо приводить ее к виду с goto. Тогда возникает вопрос: смысл???
> чтобы не быть голословным — на днях попался на глаза примерчик:
> http://rybalko.nm.ru/html/soft/dfmconv.dhtml
У меня LEX + YACC и не такие таблицы генерил. Вот только разбираться в этом... Ты серьезно думаешь, то этот код лучше понимаем, только потому что там нет goto???const
ParsTable : array [0..8,0..3] of smallint = (
{ " A # 1}
{за текст 0}( 1, 0, 4, 0),
{нач стр 1}( 3, 2, 2, 2),
{стр 2}( 3, 2, 2, 2),
{кон стр 3}( 1, 0, 6, 0),
{нач 0код 4}( 6, 6, 6, 5),
{0код 5}( 1, 0, 5, 5),
{нач 3код 6}( 6, 6, 6, 7),
{3код 7}( 1, 0, 7, 7),
{ошибка 8}( -1, -1, -1, -1)
);
{ Events handlers }
procedure WriteChar(var s, code: string; ch: char); forward;
procedure BuffChar(var s, code: string; ch: char); forward;
procedure e31(var s, code: string; ch: char); forward;
procedure e36(var s, code: string; ch: char); forward;
procedure AddCode(var s, code: string; ch: char); forward;
procedure e51(var s, code: string; ch: char); forward;
procedure e50(var s, code: string; ch: char); forward;
procedure e55(var s, code: string; ch: char); forward;
procedure BuffCode(var s, code: string; ch: char); forward;
procedure e91(var s, code: string; ch: char); forward;
procedure e90(var s, code: string; ch: char); forward;
procedure e99(var s, code: string; ch: char); forward;
procedure NOP(var s, code: string; ch: char); forward;
procedure ERR(var s, code: string; ch: char); forward;
{ Event conditions }
const
EventTable: array [0..8,0..3] of TEventProc = (
{" A # 1}
{за текст}( WriteChar, WriteChar, NOP, WriteChar),
{нач стр }( WriteChar, WriteChar, WriteChar, WriteChar),
{стр }( BuffChar, WriteChar, WriteChar, WriteChar),
{кон стр }( e31, e31, e36, e31),
{нач 0код}( ERR, ERR, ERR, AddCode),
{0код }( e51, e50, e55, AddCode),
{нач 3код}( ERR, ERR, ERR, AddCode),
{3код }( e91, e90, e99, AddCode),
{ошибка }( NOP, NOP, NOP, NOP)
);
← →
Германн © (2010-05-14 01:51) [48]Интересно какой по счёту сей холивар на ДМ по поводу goto? И как часто они возникают? И нет ли сезонной зависимости?
Про while true и repeat until false тут обсуждения гораздо реже, но тоже интересна сезонная статистика.
:)
← →
RWolf © (2010-05-14 09:03) [49]
> Ты серьезно думаешь, то этот код лучше понимаем, только
> потому что там нет goto???
он понятнее потому, что в одной таблице на виду все состояния и учтены все варианты перехода. А goto тут просто не нужно.
← →
RWolf © (2010-05-14 09:15) [50]
> Как по мне разница только в том, что в первом случае нет
> goto, а второй вариант лаконичнее.Фокус в том, что любую
> программу с использование goto можно переписать без использования
> goto. Но встречаются случаи, когда потом мысленно, для того,
> чтобы разобраться, все равно надо приводить ее к виду с
> goto. Тогда возникает вопрос: смысл???
смысл в том, что читающему исходник проще разобраться с алгоритмом.
Если написано while state != FINAL на несколько экранов, то читателю понятно, что управление не покинет этот цикл до состояния FINAL. С goto это неочевидно.
← →
pasha_golub © (2010-05-14 09:45) [51]
> RWolf © (14.05.10 09:03) [49]
> он понятнее потому, что в одной таблице на виду все состояния
> и учтены все варианты перехода.
Ха :) На виду говорите? У меня вот после Yacc"a размер модуля 6,46Mb. Таблички себе представляете? ;) Так что, после некоторого порога уже пофигу реализация. Всё равно оком не окинешь.
← →
RWolf © (2010-05-14 09:52) [52]> [51]
безусловно, инструменты выбираются по задаче.
← →
Омлет © (2010-05-14 10:20) [53]until false быстрее while true
← →
RWolf © (2010-05-14 10:46) [54]
> Омлет © (14.05.10 10:20) [53]
одинаково. простой jmp.
← →
Mystic © (2010-05-14 11:15) [55]
> он понятнее потому, что в одной таблице на виду все состояния
> и учтены все варианты перехода.
Оно то на виду, только все достаточно однотипно. Все равно как ни крути, для понимания работы автомата нужно описание всех состоний. А тут оно размыто: часть дается таблицей, часть функцией переходов. Чтобы понять этот исходник, мне все равно приходится преобразовывать его в голове к чему-то вроде
STATE_READ_STRING:
if Input <> 0 then
begin
WriteChar();
goto STATE_READ_STRING;
end;
WriteBuff();
goto STATE_END_OF_STRING;
Ну и потом я просто проверяю отдельно каждое состояние у понимаю принцип работы автомата в целом.
Еще один минус демонстрируют функции переходов из пятого состояния (предлагаю поискать десять отличий).
procedure e51(var s, code: string; ch: char);
begin
if IsUnicode(code) then begin
BuffCode(s,code,ch);
WriteChar(s,code,cQuote);
WriteString(s,popall);
end else begin
if Length(buffer)<>0 then begin
WriteChar(s,code,cQuote);
WriteString(s,popall);
WriteChar(s,code,cQuote);
end;
WriteString(s,"#"+code);
code:= "";
WriteChar(s,code,cQuote);
end;
end;
procedure e50(var s, code: string; ch: char);
begin
if IsUnicode(code) then begin
BuffCode(s,code,ch);
WriteChar(s,code,cQuote);
WriteString(s,popall);
WriteChar(s,code,cQuote); // <---- This is difference!!!
end else begin
if Length(buffer)<>0 then begin
WriteChar(s,code,cQuote);
WriteString(s,popall);
WriteChar(s,code,cQuote);
end;
WriteString(s,"#"+code);
code:= "";
end;
WriteChar(s,code,ch);
end;
Весьма характерная черта конечный автоматов в том, что обработка состояния происходит однотипно, за исключением мелких деталей на входе. А в табличном представлении с этим есть проблемы.
Еще можно посмотреть, как этот автомат изментся в случае, когда нужны будут еще строки с шестнадцатеричными кодами: например #$0A. В случае goto нам надо будет дописать одно-два новых состояний и забыть. А тут, нам надо разбить вход A-Z a-z _ на две группы: A-F и все остальное. Которое будет заметно толко для шестнадцатеричных констант. И т. д. и т. п.
Ну и в целом, 319 строк только для того, чтобы разпарсить (неполностью) Delphi строку? Честно говоря, многовато...
← →
RWolf © (2010-05-14 12:12) [56]> Оно то на виду, только все достаточно однотипно. Все равно
> как ни крути, для понимания работы автомата нужно описание
> всех состоний. А тут оно размыто: часть дается таблицей,
> часть функцией переходов.
Все переходы сосредоточены в таблице, функции описывают только реакцию автомата в конкретном состоянии на входной символ. В этом отличие от реализации «в лоб», которая смешивает в кучу описания реакций на символ и переходов между состояниями.
Для меня главный плюс такого подхода в том, что при написании программы нельзя пропустить какое-либо сочетание состояния и входных данных, при этом все варианты представлены компактно.
Ну и потом я просто
> проверяю отдельно каждое состояние у понимаю принцип работы
> автомата в целом.
> Еще один минус демонстрируют функции переходов из пятого состояния
Согласен, исходник не идеален, я его привёл просто для предметного обсуждения.
> Весьма характерная черта
> конечный автоматов в том, что обработка состояния происходит
> однотипно, за исключением мелких деталей на входе. А в табличном
> представлении с этим есть проблемы.
Это поправимо — разбиением на подфункции, скажем.
> Еще можно посмотреть,
> как этот автомат изментся в случае, когда нужны будут еще
> строки с шестнадцатеричными кодами: например #$0A. В случае
> goto нам надо будет дописать одно-два новых состояний и
> забыть. А тут, нам надо разбить вход A-Z a-z _ на две группы:
> A-F и все остальное. Которое будет заметно толко для
> шестнадцатеричных констант.
Добавится один столбец для входного символа $, один для A-F и одна строка для нового состояния «принимаю 16-ичную константу». Обработчики событий останутся те же, с поправкой на распознавание 16-ичных чисел.
Т. е. исходник легко модифицируется и не становится при этом запутанным.
← →
Mystic © (2010-05-14 13:16) [57]
> Т. е. исходник легко модифицируется и не становится при
> этом запутанным.
Пару таких исправлений, и разобраться при таком табличном задании автомата станет крайне сложно. Есть печальный опыт, когда ты метаешься от таблицы, описывающей автомат, к функциям переходов и ничего не понимаешь :) Как по мне, такие таблицы должны генерить специальные тулзы. К тому же там куча избыточной информации и не видно общей картины. Граф мне проще нарисовать на бумаге, чем изучать матрицу смежности. В крайнем случае описать взаимосвязь вершин :)
> Для меня главный плюс такого подхода в том, что при написании
> программы нельзя пропустить какое-либо сочетание состояния
> и входных данных, при этом все варианты представлены компактно.
Избыточно представлены. А ошибиться с номером состояния, в которое надо переходить, раз плюнуть. И в этой толпе цифр разбираться что к чему крайне неприятно. С таким заданием автомата есть печальный опыт работы, после чего я плюнул на него, и стал использовать while State <> FINAL do case... Стало проще. А потом я пришел к выводы, что у меня те же goto получились, только в профиль, да еще и с лишним синтаксическим оверхедом.
Вот набросок того, как бы все эти 300 строк кода выглядели с goto:
ENTRY_POINT:
Ch := ReadChar();
if Ch = QUOTE goto INSIDE_QUOTAS;
if Ch = SHARP goto CHAR_CODE;
Fail("No string");
OUTSIDE_QUOTAS:
Ch := ReadChar();
OUTSIDE_QUOTAS_WITH_CHAR:
if Ch = QUOTE goto INSIDE_QUOTAS;
if Ch = SHARP goto CHAR_CODE;
// if Ch <> END_OF_STREAM then Fail("Extra characters");
Result := JobDone();
Exit;
INSIDE_QUOTAS:
Ch := ReadChar();
if Ch = QUOTA goto CLOSED_QUOTA;
INSIDE_QUOTAS_ACCEPT_CHAR:
if Ch in [END_OF_STREAM, END_OF_LINE] then Fail("Unterminated string");
WriteChar(Ch);
goto INSIDE_QUOTAS;
CLOSED_QUOTA:
Ch := ReadChar();
if Ch = QUOTA goto INSIDE_QUOTAS_ACCEPT_CHAR;
goto OUTSIDE_QUOTAS_WITH_CHAR;
CHAR_CODE:
CharCode := 0;
Ch := ReadChar();
// if Ch = DOLLAR then goto CHAR_HEX_CODE_LOOP;
CHAR_CODE_LOOP:
if NotDigit(Ch) then
begin
WriteChar(CharCode);
goto OUTSIDE_QUOTAS_WITH_CHAR;
end;
CharCode := CharCode * 10 + Integer(Ch) - Integer("0");
Ch := ReadChar();
goto CHAR_CODE_LOOP;
← →
RWolf © (2010-05-14 13:43) [58]Звучит убедительно.
> Как по мне, такие таблицы должны генерить специальные тулзы.
Кстати, интересно было бы глянуть на этот же алгоритм в исполнении flex+yacc, re2c или другого лексера (сам пока только присматриваюсь к этим инструментам).
← →
Mystic © (2010-05-14 14:21) [59]
> Кстати, интересно было бы глянуть на этот же алгоритм в
> исполнении flex+yacc, re2c или другого лексера (сам пока
> только присматриваюсь к этим инструментам).
Там там обычное регулярное выражение. Что стоит написать
(\^[A-Z]|["][^"]*?["]|#[0-9]+|#$[0-9A-Fa-f])+
begin
ConstType := STRING_TYPE;
ConstAsString := ParseString(yytext);
return(CONST_VALUE);
end;
А ParseString уже конечным автоматом закрыть. Мне так проще. С другой стороны, можно и там сверху прикрутить еще один LEX+YACC. Еще можно все завернуть в один единственный LEX+YACC, но там будет война с пробелами между :) Delphi не пропускает конструкциюconst XXX = "123" #13;
надо так:const XXX = "123"#13;
+ потенциальные конфликты с интерпретацией ^
← →
Andy BitOff © (2010-05-14 14:42) [60]
> Германн © (14.05.10 01:51) [48]
> но тоже интересна сезонная статистика.
Это к хаяму ;)
← →
Омлет © (2010-05-14 15:41) [61]> RWolf © (14.05.10 10:46) [54]
> > Омлет © (14.05.10 10:20) [53]одинаково. простой jmp.
Я имел в виду онтологический план. В repeat-until можно выйти на первой же итерации, даже не узнав, какое там условие цикла. А в while-do минимум 1 раз на условие наткнешься.
← →
Anatoly Podgoretsky © (2010-05-14 16:06) [62]> Омлет (14.05.2010 15:41:01) [61]
repeat-until легко залететь, это опасная ненужная конструкция. Всегда надо
использовать while. Единственная ее польза, что она не по Виртовски сделана,
многострочная, а не однострочная, в соотвествии с его идеологии.
← →
GrayFace © (2010-05-14 20:19) [63]Mystic © (13.05.10 20:45) [43]
Альтернативный вариант такой, но я не могу сказать, что он такой уж легко читаемый + производительность :)
Не, это уж слишком жесто. Я в этом случае всегда использую goto, но можно вынести то, что в скобках, во вложенную функцию.
Anatoly Podgoretsky © (14.05.10 16:06) [62]
repeat-until легко залететь, это опасная ненужная конструкция. Всегда надо
использовать while. Единственная ее польза, что она не по Виртовски сделана,
многострочная, а не однострочная, в соотвествии с его идеологии.
Так для того и сделана, залезать. В чем опастность, если 1 раз залезть точно надо? Ненужная - возможно.
А идеология, наоборот, Виртовская, только более новая - в Модуле и Обероне у него IF - END IF и т.п. (или как-то так)
← →
Mystic © (2010-05-14 20:29) [64]
> А идеология, наоборот, Виртовская, только более новая -
> в Модуле и Обероне у него IF - END IF и т.п. (или как-то
> так)
Мне кажется, что верх выразительности паскалевского синтаксиса это адский синтаксис.
← →
GrayFace © (2010-05-14 20:33) [65]А я никогда серьезным парсингом не занимался, но мне кажется, что лучше формальные грамматики без всяких автоматов.
← →
Mystic © (2010-05-14 20:47) [66]> GrayFace © (14.05.10 20:33) [65]
Конечные автоматы все равно иногда всплывают наверх. Мне проще описать все строковые константы одним регулярным выражением. Тогда она будет идти одной лексемой, но чтобы получить значение строковой константы, ее надо будет парсить ручками. Если все загонять в грамматику, то она будет чрезмерно усложнятся + возможными потенциальными конфликтами.
Плюс проблема с разделителями и комментариями. Например, если я создам что-то следующее:
\^[A-Z] return(STRING_CONST_PART)
"[^"]*" return(STRING_CONST_PART)
#[0-9]+ return(STRING_CONST_PART)
#$[A-Fa-f0-9]+ return(STRING_CONST_PART)
И потом
STRING_CONST : STRING_CONST_PART
| STRING_CONST_PART STRING_CONST
то я не уверен в отсутствии конфликтов, плюс между частями строки возможны пробелы и комментарии (которые устраняет lexer). Так что проще все эти части вынести в одно большое регулярное выражение, и потом его распарсить, чем потом разруливать.
← →
GrayFace © (2010-05-14 21:04) [67]Хотя, да, подобное я парсил конечным автоматом.
Страницы: 1 2 вся ветка
Форум: "Прочее";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];
Память: 0.62 MB
Время: 0.11 c