[an error occurred while processing this directive]
1. ЧТО ТАКОЕ LISP ?
LISP - это язык контрастов. Это один из старейших компьютерных языков, но он все еще продолжает развиваться. На LISP написано множество больших программ и этот язык является хорошим средством для профессионального программиста. В то же время LISP является отличным языком для программистов начинающих.
Большинство универсальных компьютеров и огромное число микрокомпьютеров обеспечены LISP. В результате в настоящее время существует большое число версий LISP. Несмотря на то, что не существует стандартной версии LISP, адаптация, необходимая при переносе программ с одной машины на другую, обычно легко осуществима.
Наиболее известны применения LISP в области искусственного интеллекта - программы, которые поддерживают диалог, переводят с одного языка на другой, контролируют роботов, доказывают теоремы - все эти программы были написаны на LISP. Существуют программы LISP, проектирующие интегральные микросхемы, разрабатывающие новые компьютерные языки, интегрирующие и дифференцирующие алгебраические выражения и пишущие рассказы для детей. Существуют компьютерные игры, написанные на LISP и интерактивные редакторы.
Данная книга является введением в систему LISP в среде ОС ОНИКС для ПЭВМ АГАТ. Первая часть знакомит с различными функциями LISP и показывает применение каждой функции в отдельности. Вторая часть состоит из разбора программ небольших объемов, иллюстрирующих создание реальных программных структур на LISP.
Между ними имеются образцы программ, содержащие большое количество языковых средств в реальных применениях. Одним из основных достоинств LISP является то, что этот язык хорошо поддерживает высококачественную интерактивную среду программирования. Это означает, что язык обладает хорошими средствами для вылавливания ошибок, трассировки неработающих программ и т.д. Изучение LISP включает в себя знакомство с этими свойствами языка и понимание того, как применить их к вашим конкретным задачам. Главы, в которых описываются функции печати, редактор и пакет программ трассировки, а также расширения существующих и создание новых средств будут интересны профессиональным пользователям системы.
Данное руководство предполагает, что читатель знаком с ОС ОНИКС и/или знает некоторые основы без глубокого знания о компьютерах. Знание какого-либо другого языка программирования полезно, но не обязательно при чтении данной книги. Однако, необходимо уметь пользоваться клавиатурой компьютера, чтобы набирать на ней приводимые примеры, обращая при этом внимание на количество скобок.
Т.к. LISP можно использовать в режиме калькулятора, то можно повторять изученный материал, пробуя приведенные примеры на вашем компьютере. Это особенно рекомендуется для изучения первых нескольких глав, т.к. они содержат описание синтаксиса языка и некоторые основные функции, которые позже будут широко применяться.
.цв
2. ПРЕДВАРИТЕЛЬНЫЕ СВЕДЕНИЯ.
.ов
LISP работает на ПЭВМ АГАТ с 96К памяти. Обычно это компьютер типа 7, но может быть и модель 7 с расширенной памятью. Версии LISP могут работать с некоторыми дополнениями, разработанными для микро - ЭВМ.
2.1 Локальное редактирование.
За исключением специальных случаев иной организации, LISP принимает во внимание последовательность, набранную на клавиатуре после нажатия клавиши ВВОД. До этого момента можно использовать средства локального редактирования для корректирования ошибок в напечатаннах символах и переписывания ранее выведенных на экран данных в текущую строку.
Клавиша УДАЛИТЬ
Клавиша УДАЛИТЬ (точка на функциональной клавиатуре) может быть использована для удаления символа, стоящего перед курсором (т.е. последнего напечатанного символа).
Курсором можно управлять стрелками "влево-вправо, вверх-вниз". При использовании этих стрелок на экране появляется изображение двух мигающих курсоров - один в позиции, где должен быть напечатан вводимый символ, а другой позволяет переместиться в любую часть экрана.
Клавиша КОПИРОВАНИЕ
Клавиша КОПИРОВАНИЕ (знак '=' на функциональной клавиатуре) позволяет переместить символ, определенный неуправляемым курсором, в текущую строку и таким образом позволяет повторить ввод любого текста, изображенного на экране. Это позволяет быстро просмотреть и отредактировать большие тексты.
Входные сообщения на LISP обычно имеют форму строк, состоящих из некоторых символов, заключенных в круглые скобки. LISP не начинает производить какие-либо вычисления до тех плор, пока не получит законченного выражения для обработки. Это предполагает наличие обеих скобок и использование клавиши ВВОД. Когда система готова к приему новой команды, LISP выдает подсказку: Evaluate.
ВВОД
Если клавиша ВВОД нажата до того, как закрыто нужное количество правых скобок, соответствующее числу левых скобок, LISP выдает подсказку, состоящую из последовательности стрелок. Число стрелок соответствует числу необходимых скобок.
РЕД
Полное входное сообщение может быть прервано нажатием клавиши РЕД. Эта клавиша может быть использована для отмены неправильного ввода (неважно, одной или нескольких строк) или для прерывания ненужных вычислений или неправильной печати.
УПР-N
Иногда все необходимые для просмотра данные не помещаются на экране. Обычно при такой ситуации первые несколько строк теряются, уходят за верхний предел экрана телевизора или монитора прежде чем пользователь успевает их просмотреть. Такой ситуации можно избежать, установив драйвер VDU (устройство визуального отображения) в режим, при котором устанавливается пауза после каждых 32 строк.
Одновременно нажав клавиши УПР-N и затем клавишу ВВОД, можно установить этот режим.
УПР-O
Нажав CTRL-O вы вернете компьютер в нормальный, непрерывный режим. После нажатия УПР-N компьютер делает паузу независимо от того, когда происходит заполнение экрана и ожидает, когда пользователь нажмет любую клавишу. LISP автоматически устанавливает постраничный режим работы при выведении на экран ошибочных сообщений и т.о., эти сообщения не теряются за верхней границей экрана прежде чем их успеет просмотреть пользователь. Если система неожиданно останавливает печать в середине длинного неструктурного сообщения, попробуйте нажать клавишу ВВОД.
CTRL-U
Одновременное нажатие клавиши CTRL и U удаляет все символы текущей строки.
.цв
3.ВВОДНЫЕ УПРАЖНЕНИЯ НА LISP.
.ов
Одним из основных достоинств LISP является его способность хранить и обрабатывать структурированные данные. Ниже показан пример компьютерной адресной книги, использующей некоторые из средств LISP. В данном упрощенном примере для удобства имена людей ограничены одним словом (например, TARZAN, JANE). Однако, адреса, являются структурными объектами, состоящими из некоторого числа строк информации, каждая из которых может состоять из нескольких слов. Для хранения этой информации на LISP структуры представляются написанием полных адресов и каждое подполе заключается в круглые скобки. Это устраняет возможную неоднозначность, если соответствующим образом сгруппировать 'слова'.
Например,
((Acornsoft LTD) (4A MARKET HILL) (CAMBRIDGE) (CB2 3NJ) ( ENGLAND))
Заметим, что для установления связи между этим адресом и другими, где город или страна определяются двумя словами (например, ENGLAND и CAMBRIDGE), являющимися одним подполем, заключаются в круглые скобки. Т.о., например структура может работать с адресами вида:
((WHITE SANDS MISSILE RANGE) (NEW MEXICO) ( 88002) (UNITED STATES OF AMERICA))
Здесь поля, состоящие из одного слова и поля,состоящие из нескольких слов отличаются от полей адреса CAMBRIDGE. Два приведенных примера записаны, как короткие строки, которые заканчиваются в соответствующих местах. Это прерывание отражает тот факт, что вся структура LISP записывается с использованием круглых скобок, и ограничения, основанные на количестве символов, которые могут быть изображены на экране, не имеют места.
Существуют две полезные директивы, которые связывают различные части информации друг с другом и затем обрабатывают их - PUT и GET.
Адреса могут рассматриваться как 'характеристика' имени с помощью введения команды LISP типа:
(PUT 'QUEEN 'ADDRESS '((BUCKINGHAM PALACE) (LONDON)
(ENGLAND)))
Эта команда должна быть введена в ответ на подсказку LISP 'Evaluate:'. Употребление кавычек (') будет объяснено позже - в следующей главе. Они указывают LISP , что нужно рассматривать все, что следует за ними, как текстовые данные, а не программные. После вызова PUT записанная информация может быть восстановлена с помощью:
(GET 'QUEEN 'ADDRESS)
Нужный адрес будет отыскан и выведен на экран.
Правильное употребление PUT может обеспечить построение полного списка всех адресов, подлежащих хранению. Если необходимо заменить какой-либо адрес, относящийся к какому-либо имени, новый адрес может быть введен аналогичным способом, заменив таким образом, ранее хранящуюся информацию.
Дефис (-) рассматривается в LISP также, как буквенный символ. Это означает, что мы можем использовать имена, состоящие больше чем из одного слова, используя дефис:
(PUT 'GOLDEN-WONDER 'ADDRESS '((EDINGURGH-NOUSE) (ABBEY-STREET) (MARKET-HARBOROUGH) (LEICESTERSHIRE) (ENGLAND) (GREAT BRITAIN) (EUROPE) (PLANET-EARTH)))
Напечатав некоторое количество адресов, вы должны знать каким образом их можно сохранить и при необходимости использовать в работе с компьютером. Команды SAVE и LOAD могут быть использованы, для того чтобы можно продолжать работу с того места, в котором вы произвели запись (SAVE). Вся информация будет вызвана командой LOAD.
Для файла необходимо выбрать имя. В нашем случае подходящим именем будет ADDRESS. При этом необходимо ввести команду
(SАVE 'ADDRESS)
Записанная информация может быть вызвана в работе с LISP с помщью ввода команды
(LOAD 'ADDRESS)
Необходимо отметить, что при использовании LOAD в процессе работы с LISP загружаемый файл затирает всю информацию, которая была в памяти до начала загрузки. Следовательно, если эта информация вам нужна, перед загрузкой другого файла ее нужно записать.
Вам необходимо самим попробовать ввод и просмотр нескольких адресов, используя PUT и GET для тренировки. Попробуйте установить новый адрес для какого-нибудь имени и убедитесь, что вы умеете успешно использовать команды SAVE и LOAD. Также, попробуйте ввести и сохранить другие характеристики, относящиеся к именам, например, вводя BIRTHDAY (день рождения) или PHONE-NUMBER (номер телефона) вместо ADDRESS. Очевдно, можно записать множество элементов информации, связанных с одним именем, используя очень простые фрагменты LISP. Например:
(PUT 'JULIUS 'ADDRESS '((CAESARS PALACE) (ROME))) (PUT 'JULIUS 'OCCUPATION 'EMPEROR) (PUT 'JULIUS 'PHONE-NUMBER '(EX-DIRECTORY)) (PUT 'DVLC-SWANSEA 'PHONE-NUMBER '(0792 72151))
и т.д.
В последующих главах показано, как можно устранить некоторые из ограничений приведенной выше схемы базы данных. В частности, будет показано, каким образом можно преодолеть ограничение, касающееся имени, которое в данной ссхеме представлялось как одно слово. Также будет показано как можно манипулировать с произвольными строками символов, как избежать неприятностей, касающихся знаков препинания; будут описаны функции, позволяющие печатать структуры, аналогичные приведенной выше, используя запись в несколько строк, аналогичную обычной форме записи на конвертах, а не принятую в LISP скобковую запись.
.цв
4 ПЕРЕМЕННЫЕ И КАВЫЧКИ
.ов
В примере, приведенном в главе 3, впереди всех элементов данных следует один со знаком кавычки ('). Этот символ, свидетельствует о том, что все, что следует сразу за ним, независимо от того, слово это или заключенный в скобки список элементов, является набором буквенных данных. Если кавычка отсутствует, то LISP рассматривает все как программу. Таким образом, если LISP представлен
'COLOURS или '(RED WHITE BLUE),
то на печать будут выведены буквенные данные
COLOURS или (RED WHITE BLUE)
Но если представлен как
COLOURS
без первого символа кавычки, то LISP воспримет данное слово как переменную с именем COLOURS и попытается напечатать ее значение. Большинство переменных не имеют начальных значений, и поэтому LISP отреагирует
UNDEFINED (не определено)
Оператор присваивания SETQ можно использовать для присваивания значения переменной следующим образом:
(SETQ COLOURS '(RED WHITE BLUE)) или (SETQ WHERE (GET 'JULIUS 'ADDRESS))
Как вы можете видеть, значение, хранящиеся в переменной, может быть или буквенное выражение (с предшествующим знаком кавычки), или некоторая функция LISP.
И поэтому (пока переменная не получит новое значение с помощью SETQ) ввод имени переменной без кавычки означает присвоение ей значения, присвоенного операцией SETQ). Так, например, переменная COLOURS примет значение (RED WHITE BLUE). Не обращая внимания на то, как используется SETQ для задания значения переменной COLOURS, помните, если вводится
'COLOURS,
то LISP не обрабатывает это выражение и результатом, выведенным на печать, будет одно слово COLOURS.
Аналогично ведет себя операция PUT. Однако в данном случае в качестве значений выступают имена переменных, и поэтому нет необходимости в какой-либо точной восстанавливающей функции, аналогичной GET.
Здесь необходимо отметить две вещи. Во-первых, если элемент с предшествующей ему кавычкой является списком, как в примере выше '(RED WHITE BLUE), то весь список (включая и слова и подсписки) рассматривается как буквенные данные. Во-вторых, если кавычка отсутствует (GET 'JULIUS 'ADDRESS), то LISP обрабатывает это обычным образом, а не так если бы это выражение было введено с клавиатуры. Очень часто приходится создавать новые списки, частично из буквенных данных, и частично из значений переменных и результатов вычислений. Это можно сделать, используя оператор LIST.
Представьте, что переменные были уже объявлены следующим образом (или еще лучше введите):
(SETQ FIRST 'JIM) (SETQ SECOND 'JOE) (SETQ THIRD 'JACK)
Чтобы присвоить список (JIM JOE JACK) переменной WINNERS с помощью оператора SETQ, мы можем заключить этот список в кавычки:
(SETQ WINNERS '(JIM JOE JACK))
Однако мы можем переместить буквенный список одним из созданных списков, чтобы получить тот же результат:
(SETQ WINNERS (LIST 'JIM 'JOE 'JACK)) или (SETQ WINNERS (LIST FIRST SECOND THIRD))
LIST можно использовать для создания многоуровневых списков, соединив буквенные данные и данные вычислений, как в более сложном примере
(SETQ SCORE-BOARD (LIST '(RESULTS OF COMPETITION) (LIST 'WINNER FIRST) (LIST 'RUNNER-UP SECOND)))
В разработанной среде программирования будет создан список
((RESULTS OF COMPETITION) (WINNER JIM) (RUNNER-UP JOE))
По крайней мере с точки зрения представленных средств LISP, это четкое представление результатов!
Списки, как буквенные, так и созданные имеют большое значение в программировании на языке LISP. Их можно использовать как рабочие данные, как части программы, как данные, в которых хранится информация, необходимая для дальнейшей работы, и как сообщения, вводимые на печать. Мы уже начали знакомство с их использованием. Например, список (RED WHITE BLUE) является рабочим списком данных для переменнй COLOURS, а (LIST FIRST SECOND THIRD) рассматривается как обращение к создающему списки оператору LIST и трем переменным FIRST, SECOND и THIRD. Адресами в главе 3 были списки, хранящиеся сначала как характеристики слов с использованием оператора PUT и затем записанные полностью с помощью оператора SAVE. В следующих главах мы рассмотрим происхождение и использование списков, на примере использования утилитов языка обрабатывающего списки, как язык LISP.
.цв
5 ОПРЕДЕЛЕНИЕ ПРОСТЫХ ФУНКЦИЙ
.ов
Программирование на любом языке обычно включает выполнение набора тесно связанных действий. Это поймет любой программист, который записывал хотя бы небольшое число адресов в 'адресную книгу' в главе 3. Приведем два примера, иллюстрирующие необходимость краткости в выполнении часто повторяющихся действий. Это необходимая последовательность директив оператора PUT, которые все относятся к одному и тому же свойству (в нашем примере - адрес), и соответствующие обращения к оператору GET. Это требование предъявляется при объявлении функций, как в примере:
(DEFUN FIND-ADDRESS (PERSON) (GET PERSON 'ADDRESS))
Используя директиву DEFUN, была определена функция для выполнения операции GET по нахождению хранящегося адреса. Когда это введено в машину, LISP записывает данное определение под выбранным именем FIND-ADDRESS. Любое предыдущее определение FIND-ADDRESS уничтожается. Больше ничего не происходит пока не будет обращения к функции.
(FIND-ADDRESS 'JULIUS) - это вызов функции и эквивалентен выражению (GET 'JULIUS 'ADDRESS), имеющему тот же результат для печати.
Определение описывает класс действий (в данном случае характеристику хранящегося адреса), а обращение определяет детали конкретного случая (адрес 'JULIUS). В этом примере JULIUS является аргументом 'FIND-ADDRESS. В определении функции после DEFUN следует имя функции, а затем 'список локальных переменных'. Его еще часто называют список параметров или список формальных аргументов. Локальные переменные, представленные в таком виде, являются средством передачи значений аргументов. Они могут также использоваться как рабочая память для промежуточных результатов.
Для оценки вызова функции LISP связывает значения аргументов с переменными, приведенными в списке локальных переменных. В нашем примере это означает, что переменная PERSON получит значение JULIUS. Затем LISP обрабатывает тело функции. Телом функции может быть один (как показано выше) или последовательность операторов языка LISP. Значение последней (или единственной) части тела функции сохраняется, т.к. это значение передается затем как результат вызова функции. Перед этим все переменные, описанные в этой функции восстанавливаются до первоначального состояния (до вызова функции).
Естественно, что определенная выше функция FIND-ADDRESS является очень простой и короткой, и ее использование не приведет к большому сокращению программы, которую нужно ввести, чтобы восстановить информацию. Однако, хорошие программы на языке LISP часто включают такие короткие, как это определения, чтобы структура программы была настолько четкой, насколько она является короткой. Выполнение деталей - это дело практики. Например, как хранятся и находятся адреса по какой-либо конкретной схеме, включенной в функции доступа, обращение к которым осуществляется довольно легко. Это приводит к удобочитаемой программе, т.к. мнемонические имена можно использовать для функций доступа. Затем эту программу можно легко изменить, если она подойдет для изменения представления данных.
В качестве примера определения функции, один вызов которой может привести к различным результатам, рассмотрим:
(DEFUN PUT-STATISTICS (NAME YEARS CM KILOS) (PUT NAME 'AGE YEARS) (PUT NAME 'HEIGHT CM) (PUT NAME 'WEIGHT KILOS) (LIST 'STATISTICS 'RECORDED 'FOR NAME))
Здесь вызов функции может быть (PUT-STATISTICS 'CHARLIE 33 165 72)
В качестве определения функция содержит 4 части. Первые 3 - использование PUT, последняя просто строит список, подобный (STATISTICS RECORDED FOR CHARLIE), который передается как значение PUT-STATISTICS и будет выведен на печать когда вызов функции передается непосредственно LISP.
Связь, которую осуществляет LISP между локальными переменными и значениями аргументов, является временной и длится пока обрабатывается тело функции. Это означает, что имена локальных переменных для разных функций должны быть одинаковыми. Использование разных имен не обеспечивает связь между ними. Это также означает, что если для изменения значения локальной переменной используется функция SETQ, то это значение сохраняется пока продолжается обработка той функции, которая объявила эту переменную.
Здесь необходимо отметить следующее: во-первых, функции LISP - LIST и PUT используются внутри функции PUT-STATISTICS. Вызовы Функций LISP могут быть произвольно вложенными, и при записи данных, заключенных в кавычки, в любом месте программы произойдет сбой вызова функции. Во-вторых, если при вызове PUT-STATISTICS числовые данные не были заключены в кавычки, то LISP при этом вычисляет числа 33, 165 и 72. Механизм вычислений LISP устроен так, что он выдает соответствующие числовые значения.
Далее приводится определение функции, которое является более краткой формой получения тех же результатов, и которое показывает использование функции LIST для некоторых постоянных и некоторых вычисляемых аргументов.
(DEFUN DESCRIBE (NAME) (LIST (GET NAME 'AGE) 'YEAR-OLD NAME 'OF (FIND-ADDRESS NAME) 'IS (GET NAME 'HEIGHT) 'TALLL 'AND 'WEIGHS (GET NAME 'WEIGHT) 'KILOS))
Эта программа осуществляет сбор порций информации о названном человеке как с помощью непосредственного испоьзования GET, так и с помощью обращения к функции FIND-ADDRESS, и чередует эту информауию с описательными словами для создания длинного списка.
Присущая языку LISP возможность создавать вложенные друг в друга порции информации (почти) на любом уровне - встроенные скобки, отнюдь не является признаком бедности языка LISP. Действительно, то, что на первый взгляд может показаться излишним, далее становится очевидным, т.к. заключение в скобки приводит к абсолютно точному пониманию структуры частей программы LISP. Во всех приведенных здесь примерах структура программы, заключенная в скобки, будет отражена при использовании структурированоого расположения текста. Объявление функций LISP в таком виде, отражающих уже существующие программы на языке, будет представлено и объяснено позже.
.сс
[an error occurred while processing this directive]