
KN_Vse_lektsii / algan20
.pdfАлгоритмический анализ. Лекция 20.
Программирование на Scheme
Ввод и вывод (продолжение)
Рассмотрим программу, которая читает посимвольно содержимое текстового файла и выводит его на экран.
(define (file-char)
(define in (open-input-file "in.txt") ) (define (next)
(define n (read-char in))
(display n); здесь можно было написать (write n) (if (equal? n eof) (write 'end) (next) ) )
(next) (close-input-port in) )
Обсудим особенности работы этой функции. В процессе чтения read-char возвращает либо очередной символ, либо признак конца файла, т.е. значение #<eof>. В программе мы сравниваем результат чтения с переменной eof, которая как раз этому значению и равна.
После работы с файлом рекомендуется закрывать порт.
Рассмотрим второй пример чтения из файла. Пусть требуется посчитать количество прочитанных символов. Тогда было бы логично, чтобы функция next имела параметр, где и накапливался бы результат. Но тогда структуру функции придется изменить.
Мы не получим результата, если напишем
(define (file-char-count)
(define in (open-input-file "in.txt") ) (define (next k)
(define n (read-char in))
(if (equal? n eof) k (next (+ k 1) ) ) )
(next 0 ) (close-input-port in) )
Правильная версия:
(define (file-char-count)
(define in (open-input-file "in.txt") ) (define (next k)
(define n (read-char in)) (if (equal? n eof)
(begin (close-input-port in) k) (next (+ k 1) ) ) )
(next 0) )
Если файл "in.txt" содержит информацию:
1 -2 3 4
5 6 7 8.5
abc 2e-4 1.23E+1 x y z
Тогда результат вызова функции (file-char) будет таким:
> (file-char) 1 -2 3 4 5 6 7 8.5
abc 2e-4 1.23E+1 x y z#<eof>end
Рассмотрим функцию
(define (file-data)
(define in (open-input-file "in.txt")) (define (next)
(define n (read in)) (write n)
(display " ")
(if (equal? n eof) (close-input-port in) (next))) (next))
> (file-data)
1 -2 3 4 5 6 7 8.5 abc 0.0002 12.3 x y z #<eof>
В случае, когда мы используем файлы не только для чтения, но и для записи, то применяется функция
(open-output-file path [#:mode mode-flag |
#:exists exists-flag]) |
где можно задавать режимы: 'binary или 'text, а также порядок действия с существующим файлом:
'error – приводит к ошибке, если файл уже существует.
'replace – удаляет старый файл (если он был) и создает новый.
'truncate – удаляет все старые данные в существующем файле.
'must-truncate – удаляет все старые данные в существующем файле, а если его не было – сообщение об ошибке.
'truncate/replace – попытка 'truncate; если неудачная, то
'replace.
'update – открывает имеющийся файл, сохраняя там данные, а если файла нет, то ошибка.
'can-update – открывает имеющийся файл, сохраняя там данные, а если файла нет, то он будет создан.
'append – открывает файл для добавления, при этом он обязан существовать.
Пример: функция читает файл, находит сумму имеющихся в нем целых чисел, а вещественные числа записываются другой файл.
(define (file-sum)
(define in (open-input-file "in.txt"))
(define out (open-output-file "out.txt" #:exists 'replace)) (define (iter sum)
(define n (read in)) ; (write n) (display #\space) (if (equal? n eof)
(begin (close-output-port out) sum ) (cond
((integer? n) (iter (+ n sum))) ((real? n) (write n out) (iter sum)) (else (iter sum))))
)
(iter 0) )
Имеются и более мощные средства работы с файлами в scheme.
(define (file-list)
(define in (open-input-file "in.txt")) (define lines (port->lines in)) (write lines)
(close-input-port in))
Результат ее работы
> (file-list)
("1 -2 3 4" "5 6 7 8.5 " "abc 2e-4 1.23E+1 x y z")
Получили список из строк нашего файла.
Метод конечных автоматов
В задачах обработки потоковых данных особую роль играют алгоритмы, которые по сути являются итерационными и совершают следующий шаг, зная все о своем текущем состоянии и новой к этому моменту информации.
Конечный автомат — в теории алгоритмов математическая абстракция, позволяющая описывать пути изменения состояния объекта в зависимости от его текущего состояния и входных данных, при условии, что общее возможное количество состояний конечно. Конечный автомат является частным случаем абстрактного автомата.
Существуют различные варианты задания конечного автомата. Например, конечный автомат может быть задан с помощью пяти параметров: , где:
Q — конечное множество состояний автомата;
q0 — начальное состояние автомата ();
F — множество заключительных (или допускающих)
состояний, таких что ;
Σ — допустимый входной алфавит (конечное множество допустимых входных символов), из которого формируются строки, считываемые автоматом;
δ — заданное отображение множества во множество
подмножеств Q:
(иногда δ называют функцией переходов автомата).

Автомат начинает работу в состоянии q0, считывая по одному символу входной строки. Считанный символ переводит автомат в новое состояние из Q в соответствии с функцией переходов. Если по завершении считывания входного слова (цепочки символов) автомат оказывается в одном из допускающих состояний, то слово «принимается» автоматом. В этом случае говорят, что оно принадлежит языку данного автомата. В противном случае слово «отвергается».
Конечные автоматы широко используются на практике, например в синтаксических, лексических анализаторах, и тестировании программного обеспечения на основе моделей.
Пример Пусть Q = {1,2}, , I = {1}, F = {2},
Тогда |
- конечный автомат. |
Замечание. Конечные автоматы можно изображать в виде диаграмм состояний (state diagram) (иногда их называют диаграммами переходов (transition diagram)). На диаграмме каждое состояние обозначается кружком, а переход - стрелкой. Стрелка из
p в q, помеченная словом x, показывает, что является переходом данного конечного автомата. Каждое начальное состояние распознается по ведущей в него короткой стрелке. Каждое допускающее состояние отмечается на диаграмме двойным кружком.
Пример. На диаграмме изображен конечный автомат из примера.

Пример программы, которая выделяет все комментарии из одного файла в другой.
(define (comment file-in file-out) (define in (open-input-file file-in))
(define out (open-output-file file-out #:exists 'replace)) (define (next s)
(define c (read-char in)) (if (equal? c eof)
(close-output-port out) (cond
((= s 0) (cond
((equal? c #\;) (next 1)) ((equal? c #\") (next 2)) ((equal? c #\\) (next 3)) (else (next s))))
((= s 1) (write-char c out)
(cond
((equal? c #\newline) (next 0)) (else (next s))))
((= s 2) (cond
((equal? c #\") (next 0)) (else (next s))))
((= s 3) (cond
((equal? c #\\) (next 3)) (else (next 0)))))))
(next 0))