- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
[<Separator> <Digit> {<Digit>}]
[<Exponent> [<Sign>] <Digit> {<Digit>}]
<Digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
<Sign> ::= '+' | '-'
<Separator> ::= '.'
<Exponent> ::= 'E' | 'e'
На основе этих правил можно написать функцию IsNumber, которая в качестве параметра принимает строку и возвращает True, если эта строка удовлетворяет правилам записи числа, и False, если не удовлетворяет (листинг 4.2).
Листинг 4.2. Функция для определения соответствия строки синтаксису вещественного числа// Проверка символа на соответствие <Digit>
function IsDigit(Ch: Char): Boolean;
begin
Result := Ch in ['0'..'9'];
end;
// Проверка символа на соответствие <Sign>
function IsSign(Ch: Char): Boolean;
begin
Result := (Ch = '+') or (Ch = '-');
end;
// Проверка символа на соответствие <Separator>
function IsSeparator(Ch: Char): Boolean;
begin
Result := Ch='.';
end;
// Проверка символа на соответствие <Exponent>
function IsExponent(Ch: Char): Boolean;
begin
Result := (Ch = 'E') or (Ch = 'e');
end;
function IsNumber(const S: string): Boolean;
var
P: Integer; // Номер символа выражения, который сейчас проверяется
begin
Result := False;
// Проверка, что выражение содержит хотя бы один символ — пустая строка
// не является числом
if Length(S) = 0 then Exit;
// Начинаем проверку с первого символа
Р := 1;
// Если первый символ — <Sign>, переходим к следующему
if IsSign(S[Р]) then Inc(Р);
// Проверяем, что в данной позиции стоит хотя бы одна цифра
if (Р > Length(S)) or not IsDigit(S[Р]) then Exit;
// Переходим к следующей позиции, пока не достигнем конца строки
// или не встретим не цифру
repeat
Inc(Р);
until (Р > Length(S)) or not IsDigit(S[Р]);
// Если достигли конца строки, выражение корректно — число.
// не имеющее дробной части и экспоненты
if Р > Length(S) then
begin
Result := True;
Exit;
end;
// Если следующей символ — <Separator>, проверяем, что после него
// стоит хотя бы одна цифра
if IsSeparator(S[P]) then
begin
Inc(P);
if (P > Length(S)) or not IsDigit(S[P]) then Exit;
repeat
Inc(P);
until (P > Length(S)) or not IsDigit(S[P]);
// Если достигли конца строки, выражение корректно — число
// без экспоненты
if Р > Length(S) then
begin
Result := True;
Exit;
end;
end;
// Если следующий символ — <Exponent>, проверяем, что после него
// стоит все то, что требуется правилами
if IsExponent(S[Р]) then
begin
Inc(P);
if P > Length(S) then Exit;
if IsSign(S[P]) then Inc(P);
if (P > Length(S)) or not IsDigit(S[P]) then Exit;
repeat
Inc(P);
until (P > Length(S)) or not IsDigit(S[P]);
if P > Length(S) then
begin
Result := True;
Exit;
end;
end;
// Если выполнение дошло до этого места, значит, в выражении остались
// еще какие-то символы. Так как никакие дополнительные символы
// синтаксисом не предусмотрены, такое выражение не считается
// корректным числом.
end;
Для каждого нетерминального символа мы ввели отдельную функцию, разбор начинается с символа самого верхнего уровня — <Number> — и следует правилам, записанным для этого символа. Такой способ синтаксического анализа называется левосторонним рекурсивным нисходящим анализом. Левосторонним потому, что символы в выражении перебираются слева направо, нисходящим — потому, что сначала анализируются символы верхнего уровня, а потом — символы нижнего. Рекурсивность метода на данном примере не видна, т. к. наша грамматика не содержит рекурсивных определений, но мы с этим столкнемся в последующих примерах.
Пример использования функции IsNumber содержится на компакт-диске и называется IsNumberSample.
В заключение рассмотрим альтернативный способ записи грамматики вещественного числа — графический (такой способ называется синтаксическим графом, или рельсовой диаграммой). Это направленный граф, узлами которого являются терминальные (круги) и нетерминальные (прямоугольники) символы. Двигаться от одного узла к другому можно только по линиям в направлениях, указанных стрелками. В таком графе достаточно легко разобраться, а по возможностям описания синтаксиса он эквивалентен БНФ. На рис. 4.1 показана запись синтаксиса вещественного числа с помощью рельсовой диаграммы.
Рис. 4.1. Диаграмма синтаксиса вещественного числа
В качестве самостоятельного упражнения рекомендуем нарисовать с помощью рельсовой диаграммы грамматику символа "Цифра", используемого на рис. 4.1.
4.4. Простой калькулятор
Теперь у нас уже достаточно знаний, чтобы создать простейший калькулятор, т. е. функцию, которая будет на входе принимать выражение, а на выходе, если это выражение корректно, возвращать результат его вычисления. Для начала ограничимся простым калькулятором, который умеет работать только с числовыми константами и знает только четыре действия арифметики. Изменение порядка вычисления операторов с помощью скобок также оставим на потом.
Таким образом, наш калькулятор будет распознавать и вычислять цепочки чисел, между которыми стоят знаки операции, которые над этими числами выполняются. В вырожденном случае выражение может состоять из одного числа и, соответственно, не содержать ни одного знака операции. Опишем эти правила с помощью БНФ и ранее определенного символа <Number>.
<Expr> ::= <Number> {<Operation> <Number>}
<Operation> ::= '+' | '-' | '*' | '/'
ПримечаниеВ нашей грамматике не предусмотрено, что между оператором и его операндами может находиться пробел, т.е. выражение "2 + 2", в отличие от "2+2", не удовлетворяет данной грамматике. В отсутствие лексического анализатора игнорирование пробелов и прочих разделителей (переводов строки, комментариев) является трудоемкой рутинной операцией, поэтому во всех примерах без лексического анализатора мы будем требовать, чтобы выражения не содержали пробелов.
Для написания калькулятора нам понадобятся две новых функции — IsOperator, которая проверяет, является ли следующий символ оператором, и Expr, которая получает на входе строку, анализирует ее в соответствии с указанными правилами и вычисляет результат. Кроме того, функция IsNumber сама по себе нам тоже больше не нужна — мы создадим на ее основе функцию Number, которая получает на входе строку и номер позиции, начиная с которой в этой строке должно быть расположено число, проверяет, так ли это, и возвращает это число. Кроме того, функция Number должна перемещать указатель на следующий после числа символ строки, чтобы функция Expr, вызвавшая Number, могла узнать, с какого символа продолжать анализ. Если последовательность символов не является корректным числом, функция Number возбуждает исключение ESyntaxError, определенное специально для указания на ошибку в записи выражения.
Сама по себе задача преобразования строки в вещественное число достаточно сложна, и чтобы не отвлекаться на ее решение, мы воспользуемся функцией StrToFloat из модуля SysUtils. Когда функция Number выделит из строки последовательность символов, являющуюся числом, эта последовательность передается функции StrToFloat, и преобразованием занимается она. Здесь следует учесть два момента. Во-первых, в нашей грамматике разделителем целой и дробной части является точка, a StrToFloat использует системные настройки, т.е. разделителем может быть и запятая. Чтобы обойти эту проблему, слегка изменим синтаксис и будем сравнивать аргумент функции IsSeparator не с символом ".", а с DecimalSeparator (таким образом, наш калькулятор тоже станет чувствителен к системным настройкам). Во-вторых, не всякое выражение, соответствующее нашей грамматике, будет допустимым числом с точки зрения StrToFloat, т.к. эта функция учитывает диапазон типа Extended. Например, синтаксически верное выражение "2е5000" даст исключение EConvertError, т.к. данное число выходит за пределы этого диапазона. Но пока мы остаемся в рамках типа Extended, мы вынуждены мириться с этим.
Новые функции приведены в листинге 4.3.