ТОП 10:

Программа ввода выделяет формы



Функция READ основана на работающей на системном уровне процедуре чтения (Lisp reader). Она читает s-выражение, образуемое последовательностью знаков, поступающих из файла или иного источника. Внешние устройства становятся доступными из Лисп-системы через объекты, называемые потоками (stream). На логическом уровне потоки независимо от характера внешнего устройства являются последовательностью читаемых или записываемых знаков или битов. Для ввода и вывода, как и для двустороннего обмена, существуют свои типы потоков и специальные операции.

 

 

Макросы чтения изменяют синтаксис Лиспа

Процедура чтения содержит анализатор (parser), проверяющий знаки в читаемой им последовательности. Чтение обычного алфавитно-цифрового знака никаких особых действий не требует, в то время как чтение специального знака, такого как открывающая или закрывающая скобка, пробел, разделяющий элементы, или точка, приводит к специальным действиям. Соответствие между различными знаками и действиями определяется так называемой таблицей чтения (read table), которая задает лисповские функции для знаков.

Знаки, вызывающие специальные действия, называют макрознаками или макросами чтения (read macro), поскольку их чтение требует более сложных действий. Таблица чтения доступна программисту, и он может сам определять новые интерпретации знаков и, таким образом, расширять или изменять синтаксис Лиспа.

Действие макроса чтения определяется в Коммон Лиспе при помощи обыкновенной функции. Она читает и возвращает в качестве значения форму, для построения которой она в свою очередь может предварительно использовать макросы. Определим для примера макрос чтения %, действующий так же, как апостроф. Действие блокировки вычисления пользователь может определить в виде функции, которая рекурсивно читает очередное выражение и возвращает его в составе формы QUOTE:

 

_(defun quote-блокировка (поток знаков) (list ’quote (read)))

Функция, определяющая макрос чтения, имеет в Коммон Лиспе два аргумента, первый из которых описывает поток чтения, а значением второго будет сам макрознак. В данном примере мы не использовали параметры.

Запись символов и определенных для них макроинтерпретаций в таблицу чтения осуществляется командой

 

(SET-MACRO-CHARACTER знак функция)

_(set-macro-character #\% ’quote-блокировка)

Т

Здесь запись #\% обозначает знак процента (%) как объект с типом данных знак (в дальнейшем к типам данных мы вернемся подробнее).

После этих определений можно (с точки зрения пользователя) знак процента использовать так же, как апостроф:

 

_(list %знак %процента)

(ЗНАК ПРОЦЕНТА)

_%(a %b c)

(A (QUOTE B) C)

Таблиц чтения может быть несколько, но процедура чтения использует в каждый момент времени лишь одну таблицу. Текущая таблица сохраняется как значение системной переменной *READTABLE*.

Встроенными макросами чтения в Коммон Лиспе являются:

 

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

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

’ ; возвращает очередное выражение в виде вызова QUOTE

; ; символы до конца строки считаются комментариями

\ ; выделение одиночного специального знака

| ; выделение нескольких специальных знаков |…|

“ ; строка “…”

Макрознаки нельзя использовать в составе символов наподобие обычных знаков, поскольку процедура чтения проинтерпретирует их в соответствии с таблицей как макросы чтения. Для включения таких знаков в состав имен нужно использовать специальные выделяющие знаки «\» (backslash) и «|» (bar), которые блокируют макрообработку знаков.

Здесь мы не будем подробнее рассматривать использование макросов чтения и таблиц чтения, так как их редко используют в обыкновенном программировании. Определение новых форм в языке Лисп в основном осуществляется через макросы, которые мы рассмотрим позднее.

 

Символы хранятся в списке объектов

Читая и интерпретируя знаки, процедура чтения пытается строить атомы и из них списки. Прочитав имя символа, интерпретатор ищет, встречался ли ранее такой символ или он неизвестен. Для нового символа нужно зарезервировать память для возможного значения, определения функции и других свойств. Символы сохраняются в памяти в списке объектов (object list) или массиве объектов (obarray), в котором они проиндексированы на основании своего имени. Список объектов содержит как созданные пользователем, так и внутрисистемные символы (например, CAR, CONS, NIL и т.д.). Внесение символа в список объектов называют включением или интернированием (intern).

 

 

Пакеты или пространства имен

В более новых Лисп-системах, как и в Коммон Лиспе, можно пользоваться несколькими различными списками объектов, которые называют пакетами или пространствами имен (name space). Символы из различных пространств, имеющие одинаковые имена, могут использоваться различным образом. Это необходимо при построении больших систем, при программировании различных ее подсистем программисты частенько используют одинаковые имена для различных целей. Текущее пространство имен определяется по значению глобальной системной переменной. На атомы из других пассивных пакетов можно сослаться, написав перед символом через двоеточие имя пакета:

 

пакет: символ

Перед использованием такой записи необходимо, чтобы символ, на который ссылаются, был объявлен внешней переменной пространства имен. Однако и на остальные, т.е. внутренние переменные, в принципе, можно сослаться, но более специфическим способом.

 

 

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

Для вывода выражений можно использовать функцию PRINT. Это функция с одним аргументом, которая сначала вычисляет значение аргумента, а затем выводит это значение. Функция PRINT перед выводом аргумента переходит на новую строку, а после него выводит пробел. Таким образом, значение выводится всегда на новую строку (однако, во многих Лисп-системах функция PRINT переводит строку не до, а после печати выражения и не оставляет пробела):

 

_(print (+ 2 3)

Вывод (эффект)

Значение

_(print (read))

(+ 2 3) ; ввод

(+ 2 3) ; вывод

(+ 2 3) ; значение

Как и READ, PRINT является псевдофункцией, у которой есть как побочный эффект, так и значение. Значением функции является значение его аргумента, а побочным эффектом – печать этого значения.

Лисповские операторы ввода-вывода, как и присваивания, очень гибки, поскольку их можно использовать в качестве аргументов других функций, что в других языках программирования обычно невозможно:

 

_(+ (print 2) 3)

_(setq x ’(+ 2 3))

(+ 2 3)

_(eval (print x))

(+ 2 3)

_(eval (setq y (print x)))

(+ 2 3)

_y

(+ 2 3)

PRIN1 и PRINC выводят без перевода строки

Если желательно вывести последовательно на одну строку более одного выражения, то можно использовать функции PRIN1 или PRINC. PRIN1 работает так же, как PRINT, но не переходит на новую строку и не выводит пробел:

 

_(prong (prin1 1) (prin1 2) (print 3))

Как функцией PRINT, так и PRIN1 можно выводить кроме атомов и списков и другие типы данных, которые мы рассмотрим позже, например строки, представляемые последовательностью знаков, заключенных с обеих сторон в кавычки (“). Вывод в такой форме позволяет процедуре чтения (READ) вновь прочесть выведенное выражение в виде, логически идентичном (в смысле функции EQUALP) первоначальному. Таким образом, строка выводится вместе с ограничителями:

 

_(prin1 “a b c”)

“a b c”

Более приятный вид с точки зрения пользователя можно получить при помощи функции PRINC. Она выводит лисповские объекты в том же виде, как и PRIN1, но преобразует некоторые типы данных в более простую форму. Такие выведенные выражения нельзя прочесть (READ) и получить выражения, логически идентичные выведенным. Функцией PRINC мы можем напечатать строку без ограничивающих ее кавычек и специальные знаки без их выделения:

 

_(princ “a b c”)

A b c ; вывод без кавычек

“a b c” ; результат – значение аргумента

С помощью функции PRINC можно напечатать и скобки:

 

_(prong (princ “(((”) (prin1 ’луковица) (princ “)))”))

(((ЛУКОВИЦА)))

“)))”

В различных Лисп-системах значения, возвращаемые функциями вывода могут отличаться, хотя функции и называются одинаково. Например, в Маклиспе значением функций PRINT, PRIN1 и PRINC будет Т.

 

 

TERPRI переводит строку

Вывод выражений и знаков часто желательно разбить на несколько строк. Перевод строки можно осуществить функцией PRINT, которая автоматически переводит строку перед выводом, или непосредственно для этого предназначенной функцией TERPRI (terminate printing). У функции TERPRI нет аргументов и в качестве значения она возвращает NIL:

 

_(prong (prin1 ’a) (terpri) (print ’b) (print ’c))







Последнее изменение этой страницы: 2016-09-05; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.215.182.81 (0.015 с.)