Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

MULISP6

.doc
Скачиваний:
15
Добавлен:
09.02.2015
Размер:
62.98 Кб
Скачать

File: IO.LES

CLRSCRN

В данном уроке речь пойдет об организации ввода и вывода в LISP'е.

Все программы (да, и LISP-программы тоже!) общаются с окружающей средой

при помощи средств ввода/вывода и, при отсутствии таковых, самая потрясаю-

щая программа представляла бы для нас лишь черный ящик, не производящий к

тому же никаких действий. Вследствие этого Ваше знание какого-либо языка

или системы программирования нельзя считать полным, пока не рассмотрен сей

действительно заслуживающий внимания раздел.

Как Вы знаете, ввод/вывод во всех языках программирования

представляет собой наиболее плохо выполненную и/или плохо переносимую

часть. Не составил исключения из общего правила и LISP.

Многие реализации LISP'а предоставляют достаточно широкие

возможности для организации ввода/вывода, вследствие чего это разнообразие

привело к несовместимости отдельных функций. Здесь мы рассмотрим стандарт-

ные функции LISP'а, существующие как в COMMON LISP'е, так и в muLISP'е,

если противное не отмечено особо...

CONTINUE

Ввод/вывод осуществляется в LISP'е через потоки. Стандартные

потоки ввода/вывода в COMMON LISP'е задаются глобальными переменными

*STANDARD-INPUT* и *STANDARD-OUTPUT*. Эти переменные отсутствуют в

MULISP'е, зато есть функции (RDS) и (WRS), которые назначают входной и

выходной потоки, соответственно, например,

(RDS 'D:ANIMAL)

открывает файл "D:ANIMAL.LSP" и назначает поток ввода на него.

CONTINUE

После того, как файл был открыт и назначен функцией (RDS) или

(WRS), поток ввода или вывода можно вновь назначить на консоль путем

присвоения переменной RDS или WRS, соответственно, значения NIL. В даль-

нейшем, при присвоении переменной ненулевого значения, поток вновь будет

связан с текущим открытым файлом (чтение/запись будет возобновлена с теку-

щей позиции). (RDS) и (WRS) без аргумента назначают поток ввода (вывода)

на консоль и закрывают файлы открытые для ввода (вывода). Например,

(WRS 'OUTPUT) ; Открывает файл "OUTPUT.LSP" для вывода

; и назначает вывод на него

(WRITE-STRING "Hello!") ; Выводится в OUTPUT.LSP

(SETQ WRS NIL) ; Вывод - вновь на консоль (а файл открыт)

(WRITE-STRING "How are you"); Выводится на консоль

(SETQ WRS 123) ; Вывод опять идет в файл

(WRITE-STRING "The End") ; ... туда же

(WRS) ; Файл закрыт, вывод идет на консоль.

(WRITE-STRING "Congrads!") ; С чем и поздравляем.

CONTINUE

Хотя в большинстве реализаций предоставляются возможности для

работы с двоичными файлами, наиболее часто используются функции

посимвольного ввода/вывода. Эти функции вводят/выводят объекты LISP'а,

используя их печатное представление.

----------------------

В общем случае объекты LISP'а являются достаточно сложными структу-

рами, однако для удобства пользователя LISP предоставляет возможность ра-

боты с ними в виде печатного текста, который и называется печатным предс-

тавлением объекта. Скорее всего, Вы уже не раз видели объекты LISP'а в их

печатном представлении (если, конечно, Вы не начали изучение LISP'а с это-

го абзаца). Такие функции, как PRINT, принимают объект и посылают его пе-

чатное представление в поток вывода.

Аналогично, при вводе, функция ввода (например, READ) берет

символы из потока, интерпретирует их как печатное представление

LISP-объекта и строит этот объект. Таким образом, при чтении печатного

представления получается объект, равный (в смысле EQUAL) исходному

объекту.

CONTINUE

Функции ввода

-------------

READ

(READ) считывает одно целое выражение из стандартного потока ввода

и возвращает эквивалентный связанный список. Правильно составленные

выражения с использованием либо списковых, либо точечных изображений,

либо их комбинаций, являются допустимыми входными данными для функции

READ. Пробельные символы служат только для разграничения считанных

знаков и (с других точек зрения) функцией READ игнорируются.

Ниже символу SYMB присваивается значение выражения, читаемого из

стандартного потока ввода:

(SETQ SYMB (READ))

Таким образом, READ можно рассматривать как функцию, имеющую

побочный эффект -- чтение печатного представления символа.

CONTINUE

Числа при вводе интерпретируются в соответствии со значением

глобальной переменной *READ-BASE* (изначально -- 10).

Например, после выполнения

$ (SETQ *READ-BASE* 2)

основание системы счисления для ввода принимается равным 2.

Можете попробовать что-нибудь сделать в течение этого перерыва,

например, установить основание снова равным 10 (предварительно вспомнив,

как записать 10 в двоичной системе счисления).

BREAK

CLRSCRN

$ (SETQ *READ-BASE* *PRINT-BASE*)

-- это на всякий случай...

Макро-символы при вводе обрабатываются специальным образом. Когда

LISP-READER встречает макро-символ, он выполняет некоторую функцию,

ассоциированную с данным символом. В стандартно определенный набор

входят следующие символы:

<Tab>, <Space>, <NewLine> - разделители;

\ - "Escape" -- игнорируется обычное

синтаксическое значение следующего

символа;

| - начало и конец объекта (эквивалентно '\'

перед каждой буквой определения);

( - начало ввода списка или точечной пары;

) - конец ввода списка или точечной пары;

' - возвращает следующее за ним выражение с

QUOTE;

; - начало комментария (до конца строки);

" - начало или конец строки;

Макро-символы в составе имен можно употреблять, только выделив их

с помощью '\' или '|', которые блокируют макро-обработку.

CONTINUE

Новое определение макро-символа можно осуществить используя функцию

SET-MACRO-CHAR в MULISP'е (SET-MACRO-CHARACTER в COMMON LISP'е).

В MULISP'е аргументы SET-MACRO-CHAR -- определяемый символ и выра-

жение, которое определяет требуемое действие. В качестве результата в MU-

LISP'е возвращается T в случае успеха и NIL в противном случае.

Например, ниже приведено определение для символа ' (quote):

(SET-MACRO-CHAR '\' (QUOTE (LAMBDA ()

(LIST (QUOTE QUOTE) (READ)))) )

В перерыве Вы можете попробовать сделать так, чтобы LISP при

появлении знака '~' в строке возвращал объект, полученный путем инверсии

объекта, представленного в оставшейся части строки (очень полезные

действия!) или определить символ ',' как блокирующий действие ''' (QUOTE)

(чуть полезнее, этого как раз нет в MuLISP'е, хотя это и не совсем то,

что есть в COMMON LISP'е.). Впрочем, мы не сомневаемся, что вдумчивый чи-

татель придумает множество более интересных и полезных примеров использо-

вания макро-символов...

BREAK

CLRSCRN

У нас это получилось после

(SET-MACRO-CHAR '\~ '(lambda () (reverse (read))))

(SET-MACRO-CHAR '\, '(lambda () (eval (read))))

Существуют также функции, получающие текущее определение для мак-

ро-символа:

GET-MACRO-CHARACTER -- COMMON LISP

GET-MACRO-CHAR -- MULISP

Аргумент -- символ, определение которого необходимо получить.

COMMON LISP предоставляет гораздо более широкие возможности, но мы

их здесь не будем рассматривать по причине отсутствия полноценного COMMON

LISP'а.

CONTINUE

READ-CHAR

(READ-CHAR [peek-flag]) считывает очередной элемент из потока

ввода и возвращает объект, P-имя которого состоит из этого знака. Если

peek-flag не равен NIL, элемент не извлекается из входного потока

(особенность MULISP'а)

UNREAD-CHAR

(UNREAD-CHAR) восстанавливает последний элемент, считанный из

входного потока, и возвращает NIL. Т.к. может быть восстановлен только

последний считанный элемент, вызов функции повторно без выполнения

операции считывания не будет иметь результата.

PEEK-CHAR [flag] Function

(PEEK-CHAR) считывает следующий элемент из входного потока, не

извлекая его оттуда, т.е.,

(PEEK-CHAR) <==> (PROG1 (READ-CHAR) (UNREAD-CHAR))

CONTINUE

(PEEK-CHAR T) считывает элементы из входного потока до тех пор,

пока встречается непустой элемент и выполняет для него вышеизложенную опе-

рацию.

(PEEK-CHAR <элемент>), где <элемент> - символ, считывает элементы

из входного потока до тех пор, пока не встретится элемент, равный первому

символу в Р-имени <элемента>. Во всех случаях, PEEK-CHAR восстанавливает

последний считанный из входного потока элемент во входном потоке.

Например,

$ (PEEK-CHAR 'M) COMPUTER

производит именно те действия, которые Вы можете видеть на предыдущей

строке.

CLEAR-INPUT

(CLEAR-INPUT) очищает текущий буфер ввода.

CONTINUE

READ-LINE

(READ-LINE) считывает элементы из стандартного потока ввода до тех

пор, пока не встретится <Return>, и возвращает символ , у которого Р-имя

состоит из всех считанных элементов, кроме <Return>. READ-LINE

возвращает строку такой, как она есть, не отбрасывая пустые места или

комментарии.

LISTEN

Если стандартный поток ввода содержит элементы, доступные для

ввода, (LISTEN) возвращает Т, в противном случае - NIL.

CONTINUE

Функции вывода

--------------

Функции вывода, как отмечалось ранее, выводят печатное

представление символа LISP'а в стандартный поток вывода.

PRIN1

(PRIN1 <обьект>) передает символьное представление <обьекта> в

стандартный поток вывода и возвращает <обьект> в качестве значения. PRIN1

печатает символы, используя их Р-имена. PRIN1 печатает cons'ы, используя

их списковые изображения , где это возможно, и изображения с использовани-

ем точечной нотации, где это необходимо.

Можете попробовать вывести объекты (CONS 'A '(B)) и (CONS 'A 'B) и

посмотреть, что из этого получится...

BREAK

CLRSCRN

На вывод печатных представлений символов оказывают влияние

значения следующих глобальных переменных:

*PRINT-ESCAPE* -- при T выводятся ESCAPE-символы ('\', '|' и т.п.)

NIL не выводятся

*PRINT-BASE* -- основание системы счисления, используемое при выводе

чисел.

*PRINT-POINT* -- количество знаков после запятой при выводе чисел с пла-

вающей точкой.

CONTINUE

В MULISP'е, кроме вышеприведенных, используются следующие

переменные:

*PRINT-DOWNCASE* -- T - преобразовывать при выводе буквы к нижнему

регистру;

NIL - преобразовывать при выводе буквы к

верхнему регистру;

*PRINTER-ECHO* -- T - дублировать вывод на принтер

NIL - не дублировать.

В COMMON LISP'е существует ряд других переменных, предоставляющих

достаточно гибкие возможности для управления выводом, но, в силу

отмеченной ранее причины, мы не будем здесь в них углубляться.

PRINC

PRINC идентична PRIN1, кроме того, что Р-имена, содержащие специ-

альные символы, не ограничиваются разграничительными символами, причем

значение контрольной переменной *PRINT-ESCAPE* не играет роли. Эквивалент-

ное определение PRINC через PRIN1:

(DEFUN PRINC (OBJ *PRINT-ESCAPE*) (SETQ *PRINT-ESCAPE* T)

(PRIN1 OBJ) )

CONTINUE

PRINT

(PRINT обьект) передает символьное представление <обьекта> в

стандартный поток вывода, используя PRIN1, переходит на следующую строку и

возвращает <обьект> в качестве результата. Эквивалентное определение

PRINT:

(DEFUN PRINT (OBJ) (PRIN1 OBJ) (TERPRI) OBJ )

TERPRI

(TERPRI [<n>]) передает в стандартный поток вывода <n> символов

новой строки. При отсутствии <n> выводится 1 символ. В качестве

результата возвращается NIL.

Функции вывода COMMON LISP'а, отсутствующие, к сожалению, в

MULISP'е, мы не будем здесь рассматривать (наиболее существенным уроном,

по-видимому, следует считать отсутствие форматного вывода (FORMAT...)).

CONTINUE

Приведем здесь также некоторые функции, специфичные для MULISP'а:

WRITE-BYTE

Если <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 символ. В качестве результата

возвращается количество переданных символов.

CONTINUE

WRITE-STRING

WRITE-LINE

Если <символ> - символ,

(WRITE-STRING <символ>) и (WRITE-LINE <символ>)

записывают элементы Р-имени <символа> в поток вывода и возвращает <сим-

вол>. WRITE-LINE записывает элемент новой строки после пересылки Р-имени.

Если <символ> - не символ, то обе функции возвращают NIL. Следует заме-

тить, что функции COMMON LISP'а с такими же названиями выводят подстроку

заданной строки, выделяемую начальной и конечной позициями.

LINELENGTH

Если <n> - положительное целое, (LINELENGTH <n>) определяет длину

строки для вывода ее системным принтером в файле так, что строки

автоматически ограничиваются по длине <n> символами. В качестве

результата возвращается предыдущая длина строки. Если <n> неположительное

целое или не задано, функция выдает текущую длину строки. На вывод строк

на консоль данная функция не оказывает влияния.

CONTINUE

MULISP, естественно, имеет ряд функций, ориентированных на работу

с экраном консоли, которые (что также вполне естественно) специфичны

только для данной реализации и имеют малое отношение к COMMON LISP'у и

другим стандартам. Мы думаем, что Вы сможете рассмотреть их самостоятель-

но...

CONTINUE

CLRSCRN

$(RDS)

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]