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

Точно Не проект 2 / Не books / Источник_1

.pdf
Скачиваний:
9
Добавлен:
01.02.2024
Размер:
20.67 Mб
Скачать

ГЛАВА 5

ЯЗЫК ЛИСП

Вэтой и следующей главе рассматриваются две важные парадигмы программирования, используемые при построении СИИ, – функциональное

илогическое программирование. Функциональное программирование реализуется средствами языка Лисп, а логическое – языка Пролог. Важная характеристика этих языков – ориентация не на числовую обработку, а на обработку символов. Поэтому структуры данных, поддерживаемые этими языками, не ограничиваются массивами. Списки – основная структура данных, реализованная как в языке Лисп, так и в языке Пролог.

Вязыке Лисп cписки используются и для представления обрабатываемых данных, и для представления самих программ. Пролог базируется на языке исчисления предикатов и также имеет удобные средства для обработки списков. Поддержка этими языками более мощных абстракций данных значительно повышает эффективность труда программиста. Согласно данным, приведенным в [7], в десять и более раз.

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

5.1.Общая характеристика языка

Язык Лисп был разработан в конце 50-х годов Дж. Маккарти. Название языка Лисп происходит от английских слов LISt Processing – обработка списков. Существовавшие в то время языки программирования были плохо приспособлены для решения задач ИИ, так как предназначались для обработки числовой информации. Цель, поставленная Маккарти, состояла в том, чтобы создать язык, ориентированный, прежде всего, на символьную обработку, а не на числовые вычисления, а также реализовать модель вычислений, основанную на теории рекурсивных функций А. Черча [26].

Язык Лисп

211

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

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

Говоря о Лиспе, нельзя не отметить другую его характеристику. Лисп

– это язык функционального программирования. Функции являются основным инструментом для манипуляций со списками. В состав Лиспа входит большое число разнообразных функций обработки списков. Это позволяет относительно легко писать и отлаживать сложные программы.

Еще одна важная характеристика языка Лисп – это рекурсивный стиль написания многих функций. Рекурсивные определения имеют мощный математический фундамент, что позволяет выполнять аналитические исследования корректности функций, записывать их в ясной и лаконичной форме.

Сразу после появления первой версии языка начались работы по его совершенствованию. В 1961 г. была разработана новая версия языка под названием Lisp 1.5, которая претендовала на роль квазистандарта в период

с1961 по 1965 г. К началу 70-х годов появляются два новых диалекта языка Лисп – MacLisp и InterLisp. В диалекте MacLisp были введены функции

спеременным числом аргументов и возможности работы с макросами. В диалекте InterLisp появились итерационные циклы, которые позже вошли во многие другие диалекты языка. Необходимо отметить, что к этому времени уже существовало значительное число различных диалектов языка Лисп, и встал вопрос о его стандартизации. Первые усилия по стандартизации языка были предприняты в 1965 г., когда на основе Lisp 1.5 был разработан язык Standard Lisp. Это был первый диалект языка Лисп, реализованный на различных программно-аппаратных платформах. Существенное влияние на развитие языка Лисп оказало появление во второй половине 70- х годов диалекта Scheme и технологии объективно-ориентированного программирования в языках Smalltalk и KRL. В диалекте Scheme было расширено понятия области действия переменных и введено понятие лексического замыкания. К началу 80-х годов вновь остро стал вопрос о стандартизации языка. В 1981 г. агентство DARPA при Министерстве обороны США инициировало работы по разработке нового стандарта языка. В ре-

212

Глава 5

 

 

 

 

зультате этих работ в 1984 г. была разработана версия языка под названием Коммон Лисп (Common Lisp). Язык Коммон Лисп вобрал в себя достоинства многих диалектов Лиспа, существовавших к тому времени – Machine Lisp, Mac Lisp, NIL, S-1 Lisp, SpiceLisp, Scheme. Хотя различные диа-

лекты языка Лисп и оказали существенное влияние на Коммон Лисп, его не следует рассматривать как конгломерат отдельных элементов, заимствованных из них. Коммон Лисп – это высоко организованная, мощная среда программирования, содержащая весь арсенал средств, необходимый для разработки больших программных систем. Современные версии языка Коммон Лисп являются объектно-ориентированными, включают в себя среду визуального программирования и, по удобству работы, не уступают таким популярным оболочкам как DELPHI или CBuilder. Поэтому ниже будет рассматриваться именно этот диалект языка. С помощью Лиспа реализованы многие программные системы. Одна из самых известных систем – это AutoCAD фирмы AutoDesk.

5.2. Основные понятия языка

Лисп предназначен для обработки символьной информации. Поэтому основным типом данных, используемым в языке, является символьное выражение или s-выражение. Для того чтобы определить это и другие базовые понятия языка, воспользуемся расширенными металингвистическими формулами Бэкуса-Науэра (БНФ). При этом будем использовать обозначения, приведенные в таблице 5.1.

Алфавит языка Лисп состоит из цифр, букв и специальных знаков:

цифры::= 0| 1| 2| 3| 4| 5| 6| 7| 8| 9

буквы::= А| В| С … Х| Y| Z| a| b| c … x| y| z спецзнаки::= +| -| *| %| &| _| !| @| <| >| =| ?| /| {| }| ^| ~

Таблица 5.1 – Значения символов БНФ

Метасимвол

Значение

: : =

“есть по определению”

|

или (альтернатива)

{Х}*

ноль или более вхождений Х, разделяемых пробелом

{Х}+

одно или более вхождений Х, разделяемых пробелом

дальнейшее следование элементов

[Х]

ноль или одно вхождение Х

Метаимя

нетерминальный символ с именем Метаимя

 

 

Язык Лисп

213

Важное понятие языка Лисп – символы. Символы обозначают объекты, которыми манипулирует программа, и в этом смысле соответствуют именам переменных алгоритмических языков программирования. Однако символы в языке Лисп имеют и другие атрибуты, о которых будет сказано позже. Поэтому не следует их отождествлять с именами переменных алгоритмических языков программирования. Символы образуются из знаков алфавита по правилу:

символ::= буква | символ буква | символ спецзнак | символ цифра | цифра буква | цифра спецзнак | цифра символ | спецзнак символ

Примеры символов: А1, %1А, col13. В Лиспе не различаются прописные и строчные буквы. Поэтому символы, составленные из строчных букв, совпадают с соответствующими символами, составленными из прописных букв. Символы могут иметь значения. Для приписывания символам значений используются специальные формы. Изначально символы значений не имеют.

Наряду с символами, в Лиспе используются и числа. Лисп поддерживает работу с целыми, рациональными и вещественными числами:

число::=целое число | рациональное число | вещественное число

Числа записывают с помощью цифр и букв и, в отличие от символов, обозначают самих себя. Примеры чисел: 537, 2/3, 2.0Е-3.

Символы и числа представляют простейшие объекты языка Лисп, поэтому их называют атомами. Атом определяется с помощью следующей формулы:

атом::=символ | число | ()

Пара скобок ( ) в языке Лисп имеет двойное значение. С одной стороны, она обозначает пустой список, а с другой – логическое значение ложь. Иными словами, в Лиспе ложь представляется пустым списком. Все остальные значения интерпретируется как истина. Для обозначения логического значения истина (true) в Лиспе используется символ Т. Ложь или пустой список также обозначаются символом NIL. Символы Т и NIL имеют фиксированное значение, и их нельзя использовать в качестве имен других лисповских объектов.

Из атомов можно построить основную структуру данных языка Лисп

s-выражение, которое определяется с помощью следующей рекурсивной формулы:

s-выражение ::= атом | (s-выражение . s-выражение )

214

Глава 5

 

 

 

 

Приведем примеры s-выражений, составленных в соответствии с введенным определением:

gruppa

(gruppa1 . gruppa2) ((gruppa1 . gruppa2) . gruppa3)

Обратите внимание на то, что точка отделяется от знаков, стоящих слева и справа, пробелом. Конструкция (s-выражение . s-выражение) представляет собой точечную пару. Элементами точечной пары могут быть как атомы, так и точечные пары. Точечную пару можно определить с помощью следующей БНФ:

точечная пара ::= ( атом . атом )| ( атом . точечная пара )|

( точечная пара . атом )| ( точечная пара . точечная пара )

Удобно точечные пары изображать в виде диаграмм. На рисунке 5.1 показана диаграмма для точечной пары (А . В). Такая диаграмма дает наглядное представление о форме хранения точечной пары в памяти ЭВМ и называется лисповской ячейкой.

Рисунок 5.1 – Лисповская ячейка

Эта ячейка состоит из двух полей. В первом поле хранится указатель на первый элемент точечной пары, а во втором поле – указатель на второй элемент.

Если элементами точечной пары являются тоже точечные пары, то в ячейке хранятся два указателя, ссылающиеся на другие ячейки. На рисунке 5.2 изображена диаграмма для точечной пары ((А . В) . (С . D))

Сравнивая определение s-выражения и точечной пары, приходим к выводу, что

s-выражение ::= атом | точечная пара

В Лиспе выделяют специальный тип s-выражений, который называется списком. Список определяется следующим образом:

cписок ::= NIL | (( s-выражение . список ))

Язык Лисп

215

Например, s-выражения (A .(B .(C .(D . NIL)))) и ((A . B) . ((C . D) . NIL)) – списки. Элементы первого списка – это атомы A, B, C, D, элементы второго списка – это точечные пары (A . B) и (C . D) (рисунок 5.3).

Рисунок 5.2 – Диаграмма точечной пары ((A . B ) . (C . D))

В Лиспе принято также изображать список в виде последовательности элементов, отделяемых пробелом и заключенных в круглые скобки: (А В С D), ((А . В) . (С . D)). В этом случае список представляет собой упорядоченную последовательность, элементами которой являются как атомы, так и списки. Списки, записанные в такой форме, весьма удобны для представления различных моделей знаний. Вспомните, например, фреймовые модели. Список, заданный в виде последовательности элементов, всегда можно представить в точечной записи. Например, следующие записи эквиваленты:

(А В С D) ↔ (A . (B . (C . (D . NIL))))

а)

б)

Рисунок 5.3 – Диаграммы списков (А. (В . (С . (D . NIL)))) (a) и

((А . В) . ((С . D) . NIL)) (б)

216

Глава 5

 

 

 

 

Однако не каждое s-выражение, записанное в точечной форме, является списком. Например, выражение (А . (В . (C . D))) не является списком, так как не соответствует определению понятия список.

Для перехода от точечной записи выражения к записи его в форме последовательности элементов используют следующее правило. Если в s- выражении встречается точка, стоящая перед открывающейся круглой скобкой, то эту точку можно заменить пробелом, удалив упомянутую открывающуюся и парную закрывающуюся скобку. Так, s-выражение (A . (B . (C . D))) приводится к виду (А В С . D). Отсюда следует, что оно не является списком. Вместе с тем запись (А . (В . (С . (D . NIL)))) легко преобразуется к виду (А В С D) , если учесть, что атом NIL эквивалентен паре скобок (). Существует и обратное правило, позволяющее восстановить точечную запись [48].

Отметим, что некоторые записи, составленные из символов, точек и круглых скобок, не будут являться s-выражениями. Так, запись (А . В С) не может быть получена с помощью введенных выше определений, а потому не является s-выражением.

5.3.Интерпретация Лисп-программ

ВЛиспе не только данные представляются в виде s-выражений, но и программы. Выполнение программы состоит в вычислении s-выражений. Однако не любое s-выражение может быть вычислено. Вычисляемое s- выражение характеризуется определенной семантикой, которая анализируется интерпретатором.

Если результаты анализа не противоречат семантике языка, то интерпретатор вычисляет значение s-выражения. S-выражение, значение которого может быть вычислено Лисп–интерпретатором, называется формой.

На рисунке 5.4 приведена обобщенная схема интерпретации s- выражений, работа которой состоит из трех шагов. На первом шаге происходит считывание s-выражения с помощью встроенной функции READ.

Рисунок 5.4 – Схема интерпретации s-выражений

Язык Лисп

217

По умолчанию исходные s-выражения вводятся с клавиатуры. На втором шаге выполняется интерпретация s-выражения с помощью функции ЕVAL, которая является вызовом интерпретатора. На третьем шаге функция PRINT осуществляет вывод значения s-выражения. По умолчанию вычисленные значения выводятся на дисплей. Затем цикл READ-EVAL- PRINT повторяется для другого s-выражения и т.д.

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

(fn a1 a2 … an),

где fn – имя вызываемой функции; а1, а2 … аn – аргументы функции, которые задаются вычисляемыми s- выражениями.

Например, если требуется вычислить сумму чисел 5 и 3, то с клавиатуры вводится следующее s-выражение (+ 5 3). Результат интерпретации показан на рисунке 5.5. Здесь знак “+” соответствует имени функции, выполняющей сложение.

> : ( + 5 3)

; вводимое S-выражение

8

; результат

>:

; приглашение ввода

Рисунок 5.5 – Пример интерпретации

В общем случае формы могут быть заданы константами, перемен-

ными, списками. Константы соответствуют самоопределяемым формам,

которые представляют самих себя и имеют фиксированные значения (числа, символы T и NIL, строки и др.). Переменные в Лиспе обозначаются символами. При вычислении такой формы возвращается значение символа. Форма, заданная в виде списочной структуры, может представлять: вызов функции, рассмотренный выше, вызовы специальных форм, макровызовы. Специальные формы позволяют выполнять действия, недостижимые с помощью обычных функций, например, присваивать значения переменным, осуществлять условные вычисления. К таким формам относят: SETQ, QUOTE, IF и др. Макровызовы внешне соответствуют вызову функции, но отличаются по способу вычисления. Они вычисляются в два этапа: сначала из аргументов макроса строится форма, а затем она вычисляется. Подробнее макросы будут рассмотрены позже.

218

Глава 5

 

 

 

 

При вычислении s-выражений, представленных в форме констант, переменных или списков, интерпретатор EVAL реализует следующие упрощенные правила:

а) если s-выражение – это константа (самоопределяемая форма), например, число или атомы T и NIL, то EVAL возвращает такое s-выражение без изменений;

б) если s-выражение – это переменная, представленная символом, то функция EVAL возвращает последнее значение, которое было связано с этим символом; если символ не имеет значения, что выдается сообщение об ошибке;

в) если s-выражение – это список, и первый элемент списка – символ, то EVAL интерпретирует его либо как имя функции, либо как имя специальной формы, либо как имя макроса. Указанные имена должны быть известны системе. Если первый элемент списка – имя функции, то EVAL интерпретирует оставшиеся элементы списка как ее аргументы, которые подлежат вычислению с помощью EVAL.

Важным достоинством режима интерпретации является удобство отладки программ. Если случаются ошибки, то их можно тут же исправить, введя правильное s-выражение. Недостатком режима интерпретации является низкая скорость выполнения программы. Режим компиляции программ позволяет существенно улучшить этот показатель [48].

5.4.Арифметические функции

ВЛиспе имеется большое количество встроенных арифметических функций. Аргументы этих функций должны иметь числовые значения. Числа в Лиспе подразделяются на целые, вещественные и рациональные. Рациональные числа представляются в виде дроби, например, 2/3. Кроме этого, в Лиспе предусмотрена обработка комплексных чисел. Комплексное число 5+3i на языке Лисп записывается в форме #C(5 3).

Ниже приведены примеры вызовов функций, выполняющих основные арифметические операции:

(+ 2 3)

5

; сложение 2-х аргументов

(+ 3 1 2 3)

9

; сложение 2-х и более аргументов

(+

9)

9

; унарный +

(- 13 1 2 3)

7

;вычитание 2-х и более аргументов

(-

-

9)

9

;

(*

2

1.5)

3,0

; умножение 2-х аргументов

(* 2

2 3)

12

;умножение 2-х и более аргументов

(/ 7 2 2)

7/4

;деление целочисленных аргументов

Язык Лисп

 

 

 

219

(/ 7 2 2.0)

 

1.75

;деление с преобразованием

 

 

 

0.2

;к вещественному значению

(/ 5.0)

 

 

; обратное значение

(/ 2)

 

 

1/2

;

(min 2

3

1)

1

; минимум

(max 2

3

1)

3

; максимум

(abs - 5)

 

5

; абсолютное значение

(rem 17

4)

1

; остаток от деления.

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

Для сравнения скалярных чисел в Лиспе используются функции, имена которых задаются знаками отношений ( =, | = (не равно), <, >, <=, >= ). Эти функции могут иметь произвольное количество аргументов. В этом случае проверяется выполнение соответствующего отношения для всех аргументов. Если отношение выполняется, то функция возвращает в качестве результата значение Т, иначе – NIL. Например,

(> 5 4 3 2 1) Т

В Лиспе имеется большой набор иррациональных и трансцендентных функций: EXP, EXPT, LOG, SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN

и др. Аргументы тригонометрических функций задаются в радианах. Ниже приведены примеры вызовов некоторых из указанных функций:

(expt 2 10)

1024

(log 10 2)

3.321928

(sqrt -4)

#C(0.0 2.0)

Функции TRUNCATE, ROUND, FLOAT выполняют преобразование чисел из одного формата в другой. Функция TRUNCATE возвращает два значения – целую и дробную часть числа:

(truncate - 1.7) - 1 - 0.7

Функция ROUND выполняет округление числа и также возвращает в качестве результата два значения – ближайшее целое и разность между исходным значением и найденным ближайшим целым:

(round 5.7) 6 - 0.3

Функция FLOAT преобразует целое число в число с плавающей точ-

кой.

Соседние файлы в папке Не books