
- •Часть II. Программное исчисление.
- •Программы как математические объекты
- •5. Семантика cf pascal.
- •Глава 5 знакомит нас с концепцией значения программ. Для точного описания этой идеи используются такие математические структуры как строки, списки, множества, отношения и функции.
- •5.1. Строки символов.
- •5.1.1. Конкатенация строк.
- •5.1.2. Подстроки.
- •5.1.3. Композиция строк.
- •5.1.3. Декомпозиция строк.
- •5.2. Списковые структуры.
- •5.2.1. Списковые операции.
- •5.2.2. Описание файлов с помощью списков.
- •5.3. Множества
- •5.3.1. Правила описания множеств.
- •5.3.2. Операции над множествами.
- •5.3.3. Отношения и функции.
- •5.4. Значение программ.
- •5.4.2. Прямое определение значения программы.
- •5.5. Заключение
5.2.1. Списковые операции.
Списки могут обрабатываться с использованием операций, подобных тем, что мы определили для строк: конкатенация, композиция и декомпозиция, для которых используются те же символы.
Конкатенацией & двух списков является третий список, полученный присоединением второго к первому. Элементы списка L & M это элементы списка L, дополненные элементами списка M, следующими непосредственно за последним элементом L.
Композиция для списков означает присоединение элемента в конец списка. Элементы списка L x, это элементы списка L, дополненные x, следующим непосредственно за последним элементом L.
В декомпозиции результатом операции голова Θ является первый элемент в списке, если таковой имеется. Результатом операции хвост Λ является список, полученный из исходного исключением первого элемента. Операции голова и хвост не определены на пустых списках.
Подсписки определяются аналогично строкам через операцию конкатенации. L является подсписком M, если и только если существуют списки X и Y, такие что
M = X & L & Y
Следующая таблица обобщает наши знания о списковых операциях.
Операция |
Символ |
Пример |
Конкатенация |
& |
<A> & <A, B> = <A, A, B> <†12†, <A>> & <X> = <†12†, <A>, X> |
Композиция |
|
<A> <A, <A> <A, <A> ††<A,†† <> V<V <> ††><<††> |
Голова |
Θ |
Θ<X, <X>> = X |
Хвост |
Λ |
Λ<X, <X>> = <<X>> |
Хотя для обозначения порядка элементов в списке используются целые числа, концепция списка не нуждается в натуральных числах. Концепция порядка в последовательности проще и намного примитивнее, чем концепция натуральных чисел. Чтобы определить, какой из двух элементов k и m предшествуют в списке Q сформируем последовательность
Θ Q, Θ(Λ Q), Θ(Λ (Λ Q)), …
и определим, где какой из элементов появляется первым k или m.
5.2.2. Описание файлов с помощью списков.
Список как математическая идея хорошо при описании файлов. Рассмотрим INPUT. При выполнении программы, нам необходимо знать только две вещи про INPUT: какая часть данных уже прочитана и какая осталась. В таблицах выполнения это разделение отмечалось курсором.
Если рассмотреть содержимое INPUT как 2-список символьных строк, это формализует разделение на прошлое/будущее и позволяет точно рассуждать о том, что произойдет при выполнении оператора READ. Представим, что INPUT это 2-список L, состоящий из символьных строк. Первая, L1 уже считана (строка прошлого), вторая , L2 – то, что осталось (строка будущего). Таким образом, INPUT представлен списком < L1, L2>. Например, если в таблице выполнения строка символов ABC, тогда
L1 = †A†, L2 = †BC† /
где ./ - используемый нами символ маркера конца строки.
Списковые операции могут быть использованы для того, чтобы дать следующее описание выражениям READ и WRITE для одного символа.
Пусть содержимое INPUT список L = < L1, L2>, где L2 ††. Тогда оператор READ(Ch) имеет следующее значение.
Ch присваивается значение Θ L2
Содержимое INPUT становится < L1Θ L2, Λ L2>
В вышеприведенном примере для READ(Ch), Ch будет иметь следующее значение.
Θ L2 = Θ(†BC† /) = B
а INPUT будет иметь следующее значение:
< L1Θ L2, Λ L2>
= <†A†Θ (†BC† /), Λ (†BC† /)>
= <†A†B), †C† /)>
= <†A B†), †C† /)>
Или, пусть содержимое OUTPUT будет <L1, ††> и v – значение Ch. Тогда WRITE(Ch) имеет следующее значение: содержимое OUTPUT становится <L1 v, ††>.
Описание файла как 2-списка достаточно точно, но оно не адекватно для описания требований CF Pascal к файловым операциям. Например, оператор READ не может быть выполнен для OUTPUT. Файловые переменные описанные как TEXT, могут считываться и записываться, но в соответствии с определенными правилами. Например, последовательность
REWRITE(F1);
WRITE(F1, Ch);
WRITELN(F1);
RESET(F1);
READ(F1, Ch);
Является допустимой, но
REWRITE(F2);
WRITE(F2, Ch);
READ(F2, Ch);
допустимой не является, потому что к F2 перед выполнением оператора READ должно быть применен оператор RESET.
Для того, чтобы зафиксировать использование фалов формально, в описание файла нужно ввести дополнительную информацию, поэтому мы расширим 2-список до 3-списка, содержащего строку прошлого, строку будущего и состояние файла, которое может быть символом R или W, которые обозначают доступен ли файл для чтения или для записи.
Полные правила для состояний фалов даны в следующей таблице переходов, где пустые ячейки означают недопустимую операцию.
Исходное состояние |
Применяемая операция и новое состояние файла |
|||
RESET |
REWRITE |
READ/READLN |
WRITE/WRITELN |
|
R |
R |
W |
R |
|
W |
R |
W |
|
W |
Например, если состояние файла R, выражение REWRITE переводит его в состояние W. Пустая ячейка под READ/READLN c строке W означает, что в состоянии W к файлу неприменимы операции чтения.
В следующей таблице приведены значения для выражений READ и WRITE и использованием значений файлов в форме 3-списка. Таблица не описывает недопустимые комбинации.
|
F1 |
Ch (изначально v) |
|
до |
после |
||
REWRITE(F1) |
<x, y, t> |
<††, ††, W> |
|
WRITE(F1, Ch) |
<x, ††, W> |
<xv, ††, W> |
v |
WRITELN(F1, Ch) |
<x, ††, W> |
<xv, ††, W> |
v |
RESET(F1) |
<x, y, t> |
<††, x&y, R> |
|
READ(F1, Ch) |
<x, y, R> |
<xΘ(y, Λ(y, R> |
Θ(y |
READLN(F1) |
<x, (y/)&z, R> (y не содержит /) |
<x&(y/), z, R> |
Θ(y |
Например, первая строка в таблице представляет значение выражения REWRITE. Оно преобразует состояние <x, y, t> в <††, ††, W> для любых строк x, y и состояния файла t.
Таблица выполнения ниже демонстрирует успешные операции над файлами в форме 3-списка.
|
F1 |
Ch |
VAR Ch:CHAR; F1: TEXT; BEGIN Ch := ‘A’; REWRITE(F1); WRITE(F1, Ch); Ch := ‘B’; WRITELN(F1); RESET(F1); READ(F1, Ch); READ(F1, Ch); END. |
?
<††, ††, W> <†A†, ††, W>
<†A†, ††, W> <††, †A† R> <†A†, †† R> <†A†, †† R> |
?
A
B
A □ |
После первого оператора READ EOLN принимает значение TRUE, после последнего оператора READ EOF становится TRUE, и если бы программа выполнила еще один оператор READ, то произошло бы аварийное завершение ее работы.