Вообще, в тему "goto": хотя я всегда придерживался идеи "если сильно хочется, то можно" и не имею ничего против "корректного" использования "goto", я уже лет 10 его не использую совсем. никогда. (если язык позволяет этого избежать, разумеется).
Eten написал:
Если найдете способ написать весь этот алгоритм разбора числа формата целого и десятичного (пример 2 2.3 2.3e+3 и еще три, всего шесть вариантов), без goto так, чтобы не занимало циклов больше, чем данный алгоритм и было также логичным по ходу действия.
Eten, реализация конечного автомата лексического анализатора занимает совсем немного) Без всяких goto. Если не забросишь - научишься всему)
GrAndrey написал:
В указанном случае можно подключить библиотеку регулярных выражений и упростить этот код до одной строки.
Вот уж где регэкспы ни к чему)) Поскольку "замедлят" скорость выполнения кода как минимум в десятки (наверняка, сотни) раз. С другой стороны, и приведённая Eten'ом реализация далеко не самая удачная.
Неактивен
Вот, что значит читабельность на мой взгляд. Читабельность - это, когда приведенный код отформатирован отступами, именам переменных и объектов даны осмысленные имена и при этом не очень длинные, а также есть сопровождающие комментарие в самом коде. Однако, если код основан на обширном применении ООП, то лучше, когда при обращении к сво-вам объекта, методу и прочии частей самого объекта, само обращение написано полностью, например объект.список.текст[4] = объект.текст;.
Во всем остальном читабельный код, даже без изучения флагов и т.д. можно без проблем понять коментариям, хотя бы в общих чертах.
Неактивен
Eten написал:
Читабельность - это, когда приведенный код отформатирован отступами, именам переменных и объектов даны осмысленные имена…
Не нужно путать результат и средства достижения результата, поскольку из-за такой подмены понятий народ и скатывается к бездумному следованию неким, зачастую идеализированным, стандартам.
Неактивен
HzD_Byte:
Eten, реализация конечного автомата лексического анализатора занимает совсем немного) Без всяких goto. Если не забросишь - научишься всему)...
....приведённая Eten'ом реализация далеко не самая удачная....
Возмоно не удачная, но это только возможная. Не согласен со мной, тогда приведи лучший пример и без goto, но чтоб 6-ю вариантами форматов чисел или сделай пометки, где и как в моем коде не то или слишком широко шагнул. А то, я тоже, могу сказать, также по отношению к qsp не приведя должного сравнения. А так проверял свой кусок кода, даже на самые пакостные ситуации, так и обычные, итог код работоспособен ошибок не возникает. Заключение, с применением goto мне удалось не только упростить код, но и сократить число шагов. Так, что, раз нет весомых аргументов, то и не надо говорить, как говорится считаешь, что сделаешь, лучше, то покажи, либо не говори вообще.
А также, сколько бы стаж у тебя не был 10, 20, да хоть за 30 - это еще не о чем не говорит, потому как встречал детей, которые еще будучи семиклассниками, один из которых умудрился написать простой драйвер для монитора (или видюхи уже не вспомнишь 2-4 года назад было) и выйграл на конкурсе со своим творением. То задается вопрос причем тут стаж, раз и млад и стар может этим заниматься, главное, чтобы голова хорошо варила.
В окнец сие поста. Лексический анализатор не прямой, проект LASOFTGAME еще не достиг, той ситауции, когда требуется сделать прямой анализатор, т.к. нет четкой уверенности в постоянстве конструкции (в смысле не потребуются новые), а заодно такой кусок легче редактировать.
С одним соглашусь, что могут быть некоторые моменты в коде, которые можно модифицировать и сделать лучше.
Неактивен
Olegus t.Gl., в чем-то ты прав, но тоже отчасти. Намного проще читать переменную, когда знаешь откуда она и для чего (а этому помогут коменты, нормальное имя). Отступы в тексте помагют, различить где че стоит, а то, как кто-то ручкой по бумажке на коленке написал и херь поймешь, где читабельность?!
.
Вот к примеру в моем приведенном коде, если уж не взде, то хотя бы в сложных и нужных местах, у меня написан комментарий действий и их последствий, обо всем остальном догадаться можно и за так. К тому же, приведи я свой весь исходник, то ты обязательно нашел бы по нужному названию файла, класса или перечисления (и т.д.) и по коментам нужную тебе инфу. Тем паче, что инфа вне методов у меня автодокументирована xml тэгами, что кстати говоря ничуть не мешает его прочесть, вот:
/// <summary>
/// Тип ошибки
/// </summary>
protected ErrorCode er;
/// <summary>
/// Текущий читаемый символ.
/// </summary>
protected char symbol;или вот:
/// <summary>
/// Типы данных в СТК.
/// </summary>
public enum TypeVariable : int
{
/// <summary>
/// Пустой, не заданный тип данных
/// </summary>
tvNone = 0,
/// <summary>
/// Строка
/// </summary>
tvString = 1,
/// <summary>
/// Число
/// </summary>
tvNumber = 2,
/// <summary>
/// Булев
/// </summary>
tvBoolean = 3,
/// <summary>
/// Массив
/// </summary>
tvArray = 4
}последний не флаг, а перечисление, ну и чего тут не понятного, а вот теперь в коде фнукции
/// <summary>
/// Считывает следующий символ во входном коде.
/// </summary>
protected void NetxtSymbol()
{
//проверка на конец входного кода
if (this.current_pos.Line >= this.ec.Length)
{
this.Eof = true;
this.symbol = ' ';
return;
}
// Считывает символ в указанной позиции
this.symbol = this.ec[this.current_pos.Line][++this.current_pos.Column];
//кол-во считанных столбцов
this.unset_pos.Column++;
//если позиция столбца достигла конца строки, то переходим на следующую строку
//и устанавливаем перо столбца на ноль
if (this.current_pos.Column >= this.ec[this.current_pos.Line].Length - 1)
{
this.current_pos.Line++;
this.unset_pos.Line++;
this.current_pos.Column = -1;
}
}Здесь чересчур будет, приводить на каждую строку кода комент и так хватает, но будь это очень нужно, я бы написал для каждой строки, так естественно проще. Был случай, когда приходилось читать код на совершенно не знакомом языке и с помощью комента, приведенного для каждой команды, разобрать его не составило особых проблем. Вот те и читабельность. А то, что я привел не только читабельность, а еще и программирование в хорошем тоне.
Неактивен
Так никто не спорит, что отступы и всё такое улучшают читабельность. Но тогда каким образом goto выпадает из этой схемы? Есть явное указание меток, поэтому пробегая глазами по коду можно точно сказать, откуда и куда осуществляется переход. Без комментариев с окончаниями циклов и условий («}», «end», «endfor», «endif») и то сложнее разобраться. Но разумеется, если использовать goto напропалую, то глаза начнут разбегаться и про какую-либо читабельность действительно можно будет забыть.
И вообще мне кажется, что «запрет» на использование goto несколько искусственен и продвигается только для того, чтобы научить людей думать перед тем как что-то делать: продумывать логическую схему алгоритма и все точки и возможности выхода-повторения. Оператор goto в алгоритме — что-то типа «Ой» от колючки на пути. Если таких вот колючек на пути дофига, значит, не той дорогой, видимо, пошли.
И странно, что никто не поднял тему досрочного выхода из процедуры/функции: return, в середине «тела» процедуры/функции. Разве это не из этой же серии?..
Неактивен
Olegus t.Gl. написал:
продвигается только для того, чтобы научить людей думать перед тем как что-то делать: продумывать логическую схему алгоритма и все точки и возможности выхода-повторения. Оператор goto в алгоритме — что-то типа «Ой» от колючки на пути. Если таких вот колючек на пути дофига, значит, не той дорогой, видимо, пошли.
Поддерживаю. Хорошо выразил мысль. Именно это и относится к читабельности. Продуманный заранее алгоритм, состоящий из упорядоченных блоков (без гоуту) воспринимается легче.
И странно, что никто не поднял тему досрочного выхода из процедуры/функции: return, в середине «тела» процедуры/функции. Разве это не из этой же серии?..
Не совсем. return выходит из функции, которая как правило является логически завершенной частью алгоритма. А через goto путь выполнения внутри одной функции может скакать туда и обратно, усложняя структуру алгоритма.
(Кстати, множественными return'ами я лично пользуюсь постоянно).
Неактивен
Eten: вот тебе функция (is_decimal) разбора десятичного числа на языке C (почти). Без goto. Решение в лоб. Иногда самое лучшее ![]()
//---------------------------------------------------------------------------
bool is_digit (char c) // Является ли символ цифрой?
{
return c >= '0' && c <= '9';
}
//---------------------------------------------------------------------------
bool is_exp (char c) // Является ли символ значком экспоненты?
{
return c == 'e' || c == 'E';
}
//---------------------------------------------------------------------------
double read_number (char **p) // Чтение целого числа из строки
{ // (переводит стоящие подряд цифры в число)
double res = 0;
while (is_digit (**p))
{
res = res * 10 + (double) (**p - '0');
(*p)++;
}
return res;
}
//---------------------------------------------------------------------------
double read_fraction (char **p) // То же, что и read_number, только
{ // результат = число после запятой
double res = 0;
double multiplier = 1;
while (is_digit (**p))
{
multiplier *= 0.1;
res = res + multiplier * (double) (**p - '0');
(*p)++;
}
return res;
}
//---------------------------------------------------------------------------
bool is_decimal (char *p) // Чтение десятичного числа из строки.
// Возвращает признак успешности перевода строки в число
{
double int_part = 0; // Если нужно -- можно вычислить само число
double frac_part = 0; // result = (int_part + frac_part) * 10 ^ exponent
double exponent = 0; //
double exp_sign = 1;
bool int_read = false;
if (is_digit (*p)) // Число может начинаться с цифры: 123
{
int_part = read_number (&p); // Тогда читаем целую часть
int_read = true;
if (!*p)
return true; // Конец строки -- разбор успешный
}
else
if (*p != '.') // Или с точки: .456
return false; // Иначе ошибка
// Целая часть ушла. Осталась либо точка, либо 'E'
if (*p == '.') // Если точка
{
p++; // Пропускаем точку
if (!*p)
return int_read; // Конец строки -- разбор успешный, если была целая часть
}
else
if (!is_exp (*p)) // Не 'E'?
return false; // Значит ошибка
if (is_digit (*p)) // Далее дробная часть: xxx.456
{
frac_part = read_fraction (&p); // Читаем дробную часть
if (!*p)
return true; // Конец строки -- разбор успешный
if (!is_exp (*p))
return false; // Может остаться только 'E'
}
p++; // Пропускаем 'E'
if (*p == '+' || *p == '-') // После 'E' может быть знак
{
exp_sign = (*p == '+'? 1 : -1);
p++; // Пропускаем знак
}
if (!is_digit (*p)) // Далее обязательно порядок (цифра)
return false;
exponent = read_number (&p) * exp_sign; // Читаем порядок
if (*p) // На этом строка должна закончиться
return false;
return true; // Перевод успешный
}
//---------------------------------------------------------------------------Если я не допустил ошибки, разбирает любую валидную вариацию десятичного числа.
А твоя программа, кстати, что говорит на число 1.e1?
Отредактировано Gremour (03.09.2007 17:57)
Неактивен
Olegus t.Gl. написал:
Постоянно натыкаюсь на довод о читабельности, однако, народ, что вы в это вкладываете? Если взять не элегантные примеры на десяток-полтора строк, а многоэкранные листинги, то сказать, куда произойдёт переход, будет проще с goto (с его чётким указанием меток), чем если разбираться со вложенными if, for и т.п. Я уж не говорю, что придётся разгребать разного рода допонительные конструкции: флаги условия и прочее. Интересен вопрос в плане раскрытия смысла термина "читабельность"…
Вообще я сам противник вложенностей 10-го колена. Разобраться без пол-литра в этом сложно. И текст уезжает за экран. %) Такие вещи лучше разбивать на функции. 2-3 вложенности максимум.
Неактивен
Gremour
Пример хорош, но он сделан для прямого анализатора и сразу вытаскивает значение числа, также есть ситуации, в которых могут возникнуть исключительные ситуации. Мой вариант, конечно не может разобрать число 1.e1, но только пока не может. ![]()
Через дня 2-4 выложу здесь другой вариант, твоей идеи, только алгоритм будет уже мой собственный.
Неактивен
Я решал поставленню задачу. Без вычисления самого числа программа будет еще проще (можно легко модифицировать), но я решил, что число всё же нужно. Если не учитывать все варианты написания числа (вроде 1.e1) опять же алгоритм будет короче. Но почему бы их не учесть? Это вроде бы как стандарт, любой компилятор C++ понимает разные валидные вариации сокращения формы десятичного числа (опустить ноль до или после точки, опустить знак экспоненты).
Неактивен
(опустить ноль до или после точки,
Поясни на примере, плохо как-то представить такое с экспонентой понятно, только не забудь представить число и в полной обычной форме письма, чтобы понять было удобно. ![]()
Неактивен
.12 читается как 0.12
34. читается как 34.0
1.e1, соответственно, равно десяти.
Хотя, если для системы нет разницы, целое число или нет, то можно на этом не заостряться. В C/C++ описание "34." даёт компилятору информацию о том, что указанная константа в дробном формате.
Если смотреть с общечеловеческой :) позиции, то алгоритм можно упростить, пропуская только формат с цифрами по обе стороны от точки (если точка есть).
Отредактировано Gremour (05.09.2007 18:32)
Неактивен
На одном форуме наткнулся на тему, посвящённую циклу «FOR», с очень чудными мыслями:
Break - нехорошо, так что, когда надо, я использую while или изменяю значение X (лучше while, конечно).
сам операторы break и continue вобще не использую - уж очень они мне goto напоминают
Мне не нравится идея использования break в циклах, это дурной тон. Так же как continue, exit/return и пр. Точка выхода из подпрограммы должна быть одна.
Я, например, тоже склоняюсь к мысли, что лучше бы, если точка выхода из процедуры/функции одна, хотя порой это всё же приводит к излишней запутанности, но вот операторы "continue" и "break" довольно близки идеологически к естественному языку, так что, на мой взгляд, смотрятся уместно.
Неактивен
Olegus t.Gl. написал:
Я, например, тоже склоняюсь к мысли, что лучше бы, если точка выхода из процедуры/функции одна, хотя порой это всё же приводит к излишней запутанности, но вот операторы "continue" и "break" довольно близки идеологически к естественному языку, так что, на мой взгляд, смотрятся уместно.
Да и вообще, излишний пуризьм до добра не доводит. Я вот вспоминаю книжку по Паскалю (не самую плохую, кстати), где автор в главе, посвященной ООП, утверждал, что в идеале все поля объекта должны быть скрыты от программиста, и последний должен взаимодействовать с ними исключительно через методы. Т. е. типа если у объекта есть поля x, y, то писАть вот так
with object do x:=1; y:=2; end with;
не круто. Обязательно надо внутри объекта определить некий метод:
procedure SetXY(FirstVar, SecondVar: integer); virtual; begin x:=FirstVar; y:=SecondVar; end;
и впоследствии для обращения к этим полям использовать исключительно вызов
SetXY(1, 2);
К чести автора, его запала хватило только на пример, иллюстрирующий данную главу; впоследствии он прекрасно обращался к полям объекта наиболее естественным образом.
Нет, я, конечно, и сам люблю стилистически безупречные вещи, но во всем должна быть мера...
Неактивен