Россия, Пошатово |
Конечные автоматы и обработка текстов
5.2. Ввод чисел
Пусть десятичная запись числа подается на вход программы символ за символом. Мы хотим "прочесть" это число (поместить в переменную типа real его значение). Кроме того, надо сообщить об ошибке, если число записано неверно.
Более конкретно, представим себе такую ситуацию. Последовательность символов на входе делится на прочитанную и оставшуюся части. Мы можем пользоваться функцией Next:char, которая дает первый символ оставшейся части, а также процедурой Move, которая забирает первый символ из оставшейся части, переводя его в категорию прочитанных.
Будем называть десятичной записью такую последовательность символов:
а также такую: Заметим, что согласно этому определению не являются десятичными записями. Сформулируем теперь задачу точно:5.2.1. Прочесть из входной строки максимальную часть, которая может быть началом десятичной записи. Определить, является ли эта часть десятичной записью или нет.
Решение. Запишем программу на паскале (используя "перечислимый тип" для наглядности записи: переменная state может принимать одно из значений, указанных в скобках).
var state: (Accept, Error, Initial, IntPart, DecPoint, FracPart); state := Initial; while (state <> Accept) or (state <> Error) do begin | if state = Initial then begin | | if Next = ' ' then begin | | | state := Initial; Move; | | end else if Digit(Next) then begin | | | state := IntPart; {после начала целой части} | | | Move; | | end else begin | | | state := Error; | | end; | end else if state = IntPart then begin | | if Digit (Next) then begin | | | state := IntPart; Move; | | end else if Next = '.' then begin | | | state := DecPoint; {после десятичной точки} | | | Move; | | end else begin | | | state := Accept; | | end; | end else if state = DecPoint then begin | | if Digit (Next) then begin | | | state := FracPart; Move; | | end else begin | | | state := Error; {должна быть хоть одна цифра} | | end; | end else if state = FracPart then begin | | if Digit (Next) then begin | | | state := FracPart; Move; | | end else begin | | | state := Accept; | | end; | end else if | | {такого быть не может} | end; end;
Заметьте, что присваивания state:=Accept и state:=Error не сопровождаются сдвигом (символ, который не может быть частью числа, не забирается).
5.2.2. Решить предыдущую задачу с дополнительным требованием: если прочитанный кусок является десятичной записью, то в переменную val:real следует поместить ее значение.
Решение. При чтении дробной части переменная step хранит множитель при следующей десятичной цифре.
state := Initial; val:= 0; while (state <> Accept) or (state <> Error) do begin | if state = Initial then begin | | if Next = ' ' then begin | | | state := Initial; Move; | | end else if Digit(Next) then begin | | | state := IntPart; {после начала целой части} | | | val := DigitValue (Next); Move; | | end else begin | | | state := Error; | | end; | end else if state = IntPart then begin | | if Digit (Next) then begin | | | state := IntPart; val := 10*val + DigitVal(Next); | | | Move; | | end else if Next = '.' then begin | | | state := DecPoint; {после десятичной точки} | | | step := 0.1; | | | Move; | | end else begin | | | state := Accept; | | end; | end else if state = DecPoint then begin | | if Digit (Next) then begin | | | state := FracPart; | | | val := val + DigitVal(Next)*step; step := step/10; | | | Move; | | end else begin | | | state := Error; {должна быть хоть одна цифра} | | end; | end else if state = FracPart then begin | | if Digit (Next) then begin | | | state := FracPart; | | | val := val + DigitVal(Next)*step; step := step/10; | | | Move; | | end else begin | | | state := Accept; | | end; | end else if | | {такого быть не может} | end; end;
5.2.3. Та же задача, если перед числом может стоять знак - или знак + (а может ничего не стоять).
Формат чисел в этой задаче обычно иллюстрируют такой картинкой:
5.2.4. Та же задача, если к тому же после числа может стоять показатель степени десяти, как в 254E-4 ( =0.0254 ) или в 0.123E+9 ( ). Нарисовать соответствующую картинку.
5.2.5. Что надо изменить в приведенной выше программе, чтобы разрешить пустые целую и дробную части (как в " 1.", " .1 " или даже " ." - последнее число считаем равным нулю)?
Мы вернемся к конечным автоматам в "Сопоставление с образцом" (Сравнение с образцом).