2.3.1. Числа
Числа, которые можно использовать в программе, написанной на mulisp, подразделяются на целые и дробные, а целые, в свою очередь, – на малые (с абсолютной величиной менее 65536) и большие.
Любое число является простым объектом и задается с помощью четырех полей:
Табл. 2.2
IDENTITY |
SIGN |
LENGTH |
VECTOR |
Само число «знак» длина вектор
Поле IDENTITY содержит указатель самого числа, доступен как CAR, но не может быть изменен.
Содержимое поля SIGN можно выяснить, рассматривая следующую таблицу:
Табл. 2.3
SIGN |
+ |
- |
Малое целое |
NIL |
T |
Большое целое |
LAMBDA |
NLAMBDA |
Дробное |
MACRO |
SPECIAL |
SIGN доступен как CDR, но не может быть изменен.
LENGTH определена следующим образом: это величина числа, если число малое; длина слова вектора, если число большое целое; указатель на числитель, если число дробное
2.3.2 Внутреннее представление символа
Символ – распознаваемый объект, состоящий из четырех элементов-указателей
Табл. 2.4
Value |
Property |
Function |
p-name |
где Value - значение
Property - список свойств
Function - определение функции
p-name - печатное имя.
Особенностью диалекта mulisp, является то, что при создании имени в поле Value может возникнуть так называемая автоссылка, в связи с чем если не было присвоено значения некоторому символу, то оно считается совпадающим с печатным именем этого символа.
Определение функции хранится либо с использованием шаблонов машинного языка, либо на так называемом D–коде. При определении происходит псевдокомпиляция в сжатую форму, а обратный процесс проводится автоматически при обращении к функции.
Список свойств первоначально равен пустому списку NIL.
Никакие два символа не могут иметь одинаковые печатные имена. Как только тело печатного имени считано или сгенерировано, работает алгоритм хеширования, определяющий, есть ли в рабочей области атом с таким печатным именем. Если «нет», создается атом с новым печатным именем. Созданное однажды печатное имя не может быть изменено.
Задание для самостоятельной работы: Выяснить, как задаются и изменяются значения системных свойств в mulisp’е. Составить таблицу, аналогичную таблицу 2.1.
2.4. Организация ввода/вывода
Все программы (в том числе, и ЛИСП -программы) общаются с окружающей средой при помощи средств ввода/вывода. Многие реализации ЛИСПа предоставляют достаточно широкие возможности для организации ввода/вывода, вследствие чего это разнообразие привело к несовместимости отдельных функций. Мы рассмотрим стандартные функции ЛИСПа, существующие как в COMMON LISPе, так и в mulispе.
Ввод/вывод осуществляется в LISPе в процессе сеанса общения с интерпретатором, которое производится в цикле READ-EVAL-PRINT. Кроме того, ввод и вывод осуществляются при выполнении специальных функций. Ввод и вывод в ЛИСПе осуществляется через потоки. Стандартные потоки ввода/вывода в COMMON LISPе задаются глобальными переменными *STANDARD-INPUT* и *STANDARD-OUTPUT*. Эти переменные отсутствуют в mulispе, зато в нем есть функции (RDS) и (WRS), которые назначают входной и выходной потоки, соответственно, например,
(RDS 'D:RDB)
открывает файл "D:RDB.LSP" и назначает поток ввода на него.
После того, как файл был открыт и назначен функцией (RDS) или (WRS), поток ввода или вывода можно вновь назначить на консоль путем присвоения переменной RDS или WRS, соответственно, значения NIL. В дальнейшем, при присвоении переменной ненулевого значения, поток вновь будет связан с текущим открытым файлом (чтение или запись будут возобновляться с текущей позиции).
(RDS) и (WRS) без аргумента назначают поток ввода (вывода) на консоль и закрывают файлы открытые для ввода (вывода).
Потоки бывают двоичные и символьные. При выполнении различных программ наиболее часто используются функции посимвольного ввода/вывода. Эти функции вводят/выводят объекты ЛИСПа, используя их печатное представление.
Функции вывода, например, PRINT, воспринимают объект и посылают его печатное представление в поток вывода. Набор программ, выполняющих это действие в ЛИСПе называется процедурой печати, LISP-printer.
Аналогично, при вводе, функция ввода, например, READ, берет символы из потока, интерпретирует их как печатное представление ЛИСП-объекта и строит этот объект. Таким образом, при чтении печатного представления получается объект, равный (в смысле EQUAL) исходному объекту.
В идеале можно напечатать объект, а затем, прочитав напечатанное, получить тот же самый объект. На практике это часто не выполняется, порождается не тот же самый, а равный объект.
Пример
Число 27 можно представить как
27
27.
#о33
#х113
81/3
(* 3 3 3)
Список (а в) можно представить как
(а в)
( а в )
(а
|в|
)
Если в записи печатного представления допустим пробел, то допустимо и несколько пробелов (даже переход на новую строку).
Функция Print должна осуществить выбор возможного представления. Для управления действиями Print используются специальные глобальные переменные.
2.4.1. Функции ввода в mulisp
Функция READ
Функция (READ) считывает одно целое выражение из стандартного потока ввода
и возвращает эквивалентный связанный список. Правильно составленные
выражения с использованием либо списковых, либо точечных изображений,
либо их комбинаций, являются допустимыми входными данными для функции
READ. Пробельные символы служат только для разграничения считанных знаков и (с других точек зрения) функцией READ игнорируются.
Пример
В результате выполнения обращения к
(SETQ SYMB (READ))
символу SYMB присваивается значение выражения, читаемого из стандартного потока ввода.
Таким образом, READ можно рассматривать как функцию, имеющую побочный эффект – чтение печатного представления символа.
Макро-символы при вводе обрабатываются специальным образом. Когда
LISP-READER встречает макро-символ, он выполняет некоторую функцию,
ассоциированную с данным символом.
В стандартно определенный набор входят следующие символы:
<Tab>, <Space>, <NewLine> – разделители;
\ – "Escape" -- игнорируется обычное синтаксическое значение
следующего символа;
| – начало и конец объекта (эквивалентно '\' перед каждой буквой
определения);
( – начало ввода списка или точечной пары;
) – конец ввода списка или точечной пары;
' – возвращает следующее за ним выражение с QUOTE;
; – начало комментария (до конца строки);
" – начало или конец строки;
Макро-символы в составе имен можно употреблять, только выделив их с помощью '\' или '|', которые блокируют макро-обработку.
Существуют также функции, получающие текущее определение для макро-символа:
В COMMON LISP это
GET-MACRO-CHARACTER аргумент
а в mulisp
GET-MACRO-CHAR аргумент
где аргумент – символ, определение которого необходимо получить.
Функция READ-CHAR
(READ-CHAR [peek-flag]) считывает очередной элемент из потока
ввода и возвращает объект, печатное имя которого состоит из этого знака. Если
peek-flag не равен NIL, элемент не извлекается из входного потока (особенность MULISP'а)
Функция UNREAD-CHAR
(UNREAD-CHAR) восстанавливает последний элемент, считанный из
входного потока, и возвращает NIL. В связи с тем, что может быть восстановлен только
последний считанный элемент, вызов функции повторно без выполнения
операции считывания не будет иметь результата.
Функция PEEK-CHAR
(PEEK-CHAR [флаг]) считывает следующий элемент из входного потока, не
извлекая его оттуда, т.е.,
(PEEK-CHAR) <==> (PROG1 (READ-CHAR) (UNREAD-CHAR))
(PEEK-CHAR T) считывает элементы из входного потока до тех пор,
пока встречается непустой элемент и выполняет для него вышеизложенную операцию.
Обращение (PEEK-CHAR <элемент>), где <элемент> – символ, вызывает считывание элементов из входного потока до тех пор, пока не встретится элемент, равный первому
символу в печатном имени <элемента>.
Во всех случаях, PEEK-CHAR восстанавливает последний считанный из входного потока элемент во входном потоке.
Пример
$ (PEEK-CHAR 'M) COMPUTER
производит именно те действия, которые Вы можете видеть на предыдущей
строке.
Функция CLEAR-INPUT
При обращении (CLEAR-INPUT) очищается текущий буфер ввода.
Функция READ-LINE
(READ-LINE) считывает элементы из стандартного потока ввода до тех
пор, пока не встретится <Return>, и возвращает символ , у которого печатное имя
состоит из всех считанных элементов, кроме <Return>.
READ-LINE возвращает строку такой, как она есть, не отбрасывая пустые места или комментарии.
2.4.2 Функции вывода в mulisp
Функции вывода, как отмечалось ранее, выводят печатное представление символа ЛИСПа в стандартный поток вывода.
Функция PRIN1
(PRIN1 <обьект>) передает символьное представление <обьекта> в
стандартный поток вывода и возвращает <обьект> в качестве значения.
PRIN1 печатает символы, используя их печатные имена. PRIN1 печатает точечные пары (cons'ы), используя их списковые изображения, где это возможно, и изображения с использованием точечной нотации, где это необходимо.
На вывод печатных представлений символов оказывают влияние значения следующих глобальных переменных:
*PRINT-ESCAPE* – при значении T выводятся ESCAPE-символы ('\', '|' и т.п.),
при значении NIL не выводятся
*PRINT-BASE* – основание системы счисления, используемое при выводе
чисел.
*PRINT-POINT* – количество знаков после запятой при выводе чисел с
плавающей точкой.
Функция PRINC идентична PRIN1, кроме того, что печатные имена, содержащие специальные символы, не ограничиваются разграничительными символами, причем
значение контрольной переменной *PRINT-ESCAPE* не играет роли.
Функция PRINT
При выполнении обращения (PRINT обьект) символьное представление <обьекта> передает в стандартный поток вывода, с использованием PRIN1, осуществляется переход на следующую строку и возвращение <объекта> в качестве результата. Эквивалентное определение PRINT:
(DEFUN PRINT (OBJ) (PRIN1 OBJ) (TERPRI) OBJ )
Функция TERPRI
(TERPRI [<n>]) передает в стандартный поток вывода <n> символов
новой строки. При отсутствии <n> выводится 1 символ. В качестве
результата возвращается NIL.
Функция WRITE-BYTE
Это функция, специфичная для MULISPа.
Если <n> - целое число в пределах от 0 до 255 включительно,
(WRITE-BYTE <n>) записывает байт с ASCII кодом <n> в стандартный поток
вывода и возвращает <n>.
Замечание. Примерно эквивалентная функция в COMMON LISPе
называется WRITE-CHAR и выводит знак,
т.е. (WRITE-BYTE <n>) <==> (WRITE-CHAR (ASCII <n>))
Функция SPACES
(SPACES [<n>]) передает в стандартный поток вывода <n> символов
пробела. При отсутствии <n> выводится 1 символ. В качестве результата
возвращается количество переданных символов.
Функции WRITE-STRING и WRITE-LINE
Если <символ> – символ, то
(WRITE-STRING <символ>) и (WRITE-LINE <символ>)
записывают элементы печатного имени <символа> в поток вывода и возвращает <сим-
вол>. WRITE-LINE записывает элемент новой строки после пересылки Р-имени.
Если <символ> - не символ, то обе функции возвращают NIL. Следует заме-
тить, что функции COMMON LISP'а с такими же названиями выводят подстроку
заданной строки, выделяемую начальной и конечной позициями.
Функция LINELENGTH
Если <n> - положительное целое, (LINELENGTH <n>) определяет длину
строки для вывода ее системным принтером в файле так, что строки
автоматически ограничиваются по длине <n> символами. В качестве
результата возвращается предыдущая длина строки. Если <n> неположительное
целое или не задано, функция выдает текущую длину строки. На вывод строк
на консоль данная функция не оказывает влияния.