Вычислительные машины первоначально разрабатывались для осуществления численных вычислений и обработки больших объемов данных. Численным вычислениям свойственно выполнение большого числа алгоритмов или сложных, но заранее точно определённых вычислений над сравнительно простыми однообразными элементами данных - числами, векторами, массивами и т.п. Наиболее эффективные суперкомпьютеры способны выполнять обескураживающее количество операций в секунду. Численные вычисления хорошо подходят для решения научно-технических задач, их понятия количественно измеримы и точно представимы в числовой форме.
В свою очередь в экономических задачах обрабатывается большое количество данных, состоящих часто из элементов данных различных типов, которые, однако, регулярны по форме и относятся к количественным типам данных, таких, например, как записи, файлы и базы данных. Осуществляемые действия сравнительно просты, но программы могут быть довольно велики.
Вычислительные машины, ориентированные на решение описанных выше задач, и языки программирования не подходят столь же хорошо для символьной обработки, которая имеет дело со сложными структурами данных и базами знаний, содержащими правила принятия решений и другие многообразные объекты. Символьная обработка позволяет эффективно работать с такими структурами, как предложения естественного языка, значения слов и предложений, нечеткие понятия и т.д., и на их основе принимать решения, проводить рассуждения и осуществлять другие, свойственные человеку способы обращения с данными. В качестве типичного примера служат экспертные системы, содержащие профессиональные знания по некоторой специальности, программы, работающие с естественным языком и т.д.
В этих применениях предполагается представление в подходящей форме символьных и данных со сложной структурой. Работа с ними часто ведётся в заранее непредсказуемых ситуациях. Характерно, что кроме сложной структуры таким данным свойственно разнообразие форм их выражения. Большая часть объектов данных конкретной проблемной области может иметь отличное от других индивидуальное строение. Однако при их обработке поведение программы определяется на основе задаваемых на более общем уровне принципов, законов и правил, а также на основе типов ситуаций и образцов, распознаваемых в этих ситуациях.
Например, в играющей в шахматы программе невозможно заранее учесть все позиции. Анализ игры осуществляется на основе классификации позиций, распознавания стандартных позиций, определения характеристик позиций, построения оценок текущей позиции и использования ограниченного набора стратегий, правил принятия решения и т.д. Так программа может оценить такие позиции, которые программист специально не предусматривал. При удачном стечении обстоятельств программа может победить своего создателя.
С помощью структур, имеющих множество форм представления, стало возможным решать задачи, которые ранее считались практически или даже принципиально неразрешимыми. Наибольшие ограничения в создании разумных программ накладывает недостаточное знание нами механизмов принятия решений, строения знаний об естественном языке и о других областях, а также уровень нашего мастерства в программировании.
Лисп является наиболее важным языком программирования, используемым в исследованиях по искусственному интеллекту и в математической лингвистике. Название языка "Лисп" происходит из "list processing" (обработка списков). Буквально английское слово "lisp" означает "лепетать", "шепелявить" и "сюсюкать". В качестве имени языка программирования искусственного интеллекта это метко, поскольку именно с помощью Лиспа вычислительные машины научились в некоторой мере лепетать на человеческом языке.
И всё же Лисп ни в каком смысле не является младенцем. Скорее он старик среди языков программирования. Лисп разработан уже в 50-х годах и является вслед за Фортраном старейшим ещё используемым языком программирования. Но в отличие от Фортрана будущее Лиспа ещё впереди.
Лисп представляет собой язык так называемого функционального программирования. Он основан на алгебре списочных структур, лямбда-исчислении и теории рекурсивных функций. Благодаря неразвитости традиционной вычислительной техники, отличающемуся от других языков программирования характеру и из-за наличия элементарных средств обработки списков Лисп долгое время являлся основным инструментом исследователей искусственного интеллекта и средством теоретического подхода к анализу программирования. Однако в 80-х годах Лисп, наконец, вышел из лабораторий и нашел применение в прикладных проблемах.
Обычно языки программирования не изобретают, а проектируют. Однако по отношению к Лиспу можно говорить об изобретении. Первоначальная версия языка, в частности, содержала множество понятий и принципов, которые сначала казались очень странными, но многие из которых позже оказались существенным нововведением. Кроме этого, возможность добавления в Лисп в течение десятилетий многих новых черт подтвердила свойство расширяемости этого языка. Вокруг Лиспа возникла широкая культура, охватывающая многочисленные школы и разнообразные диалекты языка, различные системы и методы программирования, программные среды и Лисп-машины.
Далее мы коротко рассмотрим многочисленные свойства Лиспа и его отличия от других языков программирования.
В Лиспе формы представления программы и обрабатываемых ею данных одинаковы. И то и другое представляется списочной структурой, имеющей одинаковую форму. Таким образом программы могут обрабатывать и преобразовывать другие программы и даже самих себя. В процессе трансляции можно введённое и сформированное в результате вычислений выражение данных интерпретировать в качестве программы и непосредственно выполнить. Предоставленная этим возможность так называемого программирования, управляемого данными, и непосредственное влияние программы на программу тесно связаны между собой.
Это свойство обладает не только теоретическим, но и большим практическим значением. Единообразность представления данных и программы можно сопоставить с принципом фон Неймана, по которому программа и данные хранятся в единой памяти. Благодаря этому появилась великолепная возможность создания универсальной вычислительной машины обработки данных. Соответственно единообразное представление программы и данных и реализация программы путем интерпретации открывают совершенно новые возможности программирования.
Универсальный единообразный и простой синтаксис списка не зависит от применения, и с его помощью легко определять новые формы записи, представления и абстракции. Даже сама структура языка является, таким образом, расширяемой и может быть заново определена. В то же время достаточно просто написания интерпретаторов, компиляторов, преобразователей, редакторов и других средств. К Лиспу стоит подойти как к языку программирования, с помощью которого реализуются специализированные языки, ориентированные на приложение, и создается окружение более высокого уровня. Присущая Лиспу гибкая расширяемость не встречается в традиционных замкнутых языках программирования.
Списки, представляющие программы и данные, состоят из списочных ячеек, расположение и порядок которых в памяти несущественны. Структура списка определяется логически на основе имен символов и указателей. Добавление новых элементов в список или удаление из списка может производиться без переноса списка в другие ячейки памяти. Резервирование и освобождение могут в зависимости от потребности осуществляться динамически, ячейка за ячейкой. По-другому обстоит дело, например, при обращении со строками или массивами Фортрана.
Пользователь не должен заботится об учёте памяти. Система резервирует и освобождает память автоматически в соответствии с потребностью. Когда память кончается, запускается специальный мусорщик. Он собирает неиспользуемые символы и списки, включает их в работу путём вторичного использования. Среда Лиспа постоянно содержится в порядке. Управление памятью просто и не зависит от физического расположения, поскольку свободная память логически состоит из цепочки списочных ячеек.
В первую очередь данные обрабатываются в оперативной и виртуальной памяти, которая может быть довольно большой. Файлы используются в основном для хранения программ и данных в промежутке между сеансами.
Используемое в Лиспе так называемое функциональное программирование основывается на той простой идее, что в результате каждого действия возникает значение. Значения становятся аргументами следующих действий, и конечный результат всей задачи выдаётся пользователю.
Программы строятся из логически расчленённых определений функций. Определения состоят из управляющих структур, которые организуют вычисления и из вложенных, часто вызывающих самих себя (рекурсивных) вызовов функций. Основными средствами функционального программирования как раз и являются композиция и рекурсия.
Кроме функционального программирования в Лиспе можно использовать программирование, основанное на обычном пошаговом исполнении операторов с присваиваниями, передачами управления и специальными операторами цикла. С помощью макропрограммирования можно запрограммировать новые структуры языка или реализовать совершенно новые языки. Кроме того, в Лиспе можно применять множество методов программирования, известных из традиционных языков. В зависимости от системы в Лиспе можно использовать методы программирования более высокого уровня например такие, как объектно-ориентированное программирование, ситуационное программирование, продукционное программирование и логическое программирование. Различные методы можно применять совместно в единой интегрированной среде.
Программирование и тестирование программы осуществляется функция за функцией, которые пошагово определяются и подвергаются тестированию. Такой подход называют пошаговым программированием. Написание, тестирование и исправление программы осуществляется внутри Лисп-системы без промежуточного использования операционной системы. Специальные операционные системы более не используются, точнее они скрыты от пользователя. Все услуги имеются в интегрированной системе программирования.
Вспомогательные средства, находящиеся в распоряжении пользователя, такие как, например редактор, инспектор, транслятор, структурная печать и другие образуют общую интегрированную среду, язык которой нельзя чётко отличить от системных средств. Отдельные средства по своему принципу являются прозрачными, чтобы их могли использовать другие средства. Например, интерпретатор может вызвать редактор. Редактор - интерпретатор и так далее даже рекурсивно. Работа может производится часто на различных уровнях или в различных рабочих окнах. Такой способ работы особенно хорошо подходит для исследовательского программирования и быстрого построения прототипов.
В Лиспе имена символов, переменных, списков, функций и других объектов не закреплены предварительно за какими-нибудь типами данных. Типы в общем не связаны с именами объектов данных, а сопровождают сами объекты. Таким образом, переменные могут в различные моменты времени представлять различные объекты. В этом смысле Лисп является языком без закрепления типов.
Например, в Фортране имена переменных, начинающиеся с букв I, J и K, закреплены за целыми числами уже с момента определения языка в 50-х годах. В Паскале тип переменной закрепляется на этапе написания программы. В некоторых языках тип переменной определяется на этапе трансляции. В Лиспе тип определяется по ходу выполнения программы.
Динамическая, осуществляемая лишь в процессе исполнения, проверка типа и позднее связывание допускают разностороннее использование символов и гибкую модификацию программ. Функции можно определять практически независимо от типов данных, к которым они применяются.
Однако указанная не-закреплённость типов не означает, что в Лиспе вовсе нет данных различных типов. Мы далее увидим, что набор типов данных наиболее развитых Лисп-систем необычайно разнообразен.
Одним из общих принципов развития Лисп-систем было свободное включение в язык новых возможностей и структур, если считалось, что они найдут более широкое применение. Это было возможно в связи с естественной расширяемостью языка.
Платой за динамические типы являются действия по проверке типа на этапе исполнения. В Лисп-машинах проверка типа эффективно осуществляется на уровне аппаратуры.
Лисп является одновременно как языком прикладного, так и системного программирования. Он напоминает машинный язык тем, что как данные, так и программа представлены в одинаковой форме. Язык превосходно подходит для написания интерпретаторов и трансляторов как для него самого, так и для других языков. Например, ядро интерпретатора Лиспа, написанное на самом Лиспе, составляет пару страниц красивого кода. Примерно в том же объёме уместится и ядро интерпретатора Пролога. Как известно, наиболее короткий интерпретатор Пролога, написанный на Лиспе занимает несколько десятков строк.
Традиционно Лисп-системы в основной своей части написаны на Лиспе. При программировании, например транслятора, есть возможность для оптимизации объектного кода использовать сложные преобразования и методы искусственного интеллекта. Лисп можно в хорошем смысле считать языком машинного и системного программирования высокого уровня. И это особенно характерно для Лисп-машин, которые вплоть до уровня аппаратуры спроектированы для Лиспа и системное программное обеспечение которых написано на Лиспе.
Лисп-системы и среды программирования развиваются уже начиная с конца 50-х годов. С самого начала разработки в них был заложен принцип возможности использования отдельных средств непосредственно из интерпретатора.
В реализациях для микроЭВМ количество системных и встроенных функций обычно ограничивается порядком десяти, сотни. В больших системах разделения времени, таких как Маклисп и Интерлисп, функций многие сотни. Коммон Лисп и автоматизированные рабочие места (рабочие станции) для исследователей искусственного интеллекта и Лисп-машины предлагают особенно широкий выбор различных вспомогательных средств и функций интегрированной среды, которая, кроме всего прочего, открыта для модификаций пользователя.
Системное программное обеспечение Лисп-машин содержит более 10000 функций и десятки мегабайтов кода. Во многих системах исходные тексты кода Лисп предоставляются пользователю. Системные функции можно переопределить или модифицировать, и пользователь может свободно расширить и подогнать систему для себя.
Про Лисп говорят, что это неэффективный язык, особенно в части арифметики. Это было верно на начальном этапе. На численную сторону не обращалось внимание, поскольку язык был предназначен для символьной обработки. Теперь это уже не так. Лисп-системы могут в численных вычислениях быть более эффективными, чем Фортран на той же машине. Это объясняется в том числе и тем, что в трансляторах с Лиспа можно применять более сложные преобразования для оптимизации кода.
Естественным является то, что большие системы, предлагающие многосторонние и разумные услуги, требуют большой вычислительной мощности и громоздки для системы разделения времени. Однако это связано не с Лиспом или его плохой реализацией, а с тем вниманием и с той готовностью помочь, которые система предлагает пользователю.
Например, Интерлисп сохраняет полный перечень всех действий пользователя и полное описание более ранних состояний системы и способен автоматически исправлять многие неточности, как, например, ошибки в написании. Если эти свойства правильно использовать, то они повышают производительность профессионального программиста в большей степени, чем тратят ресурсы машины.
С точки зрения программирования критике подвергается и внешний облик языка: изобилие скобок и кажущийся беспорядок. Лисп ("Lots if Idiotic Silly Parentheses") представляется как трудно-понимаемый и трудно-изучаемый язык.
Такой подход проистекает из используемых в Лиспе функционального образа мышления и техники программирования, которые чужды программистам, привыкшим к операторному программированию традиционных языков. Естественно используемые в программировании на Лиспе структуры данных и управляющие структуры часто сложны, поскольку проблемы искусственного интеллекта из-за своей сложности предполагают сложные структуры и программы. Иерархические списочные структуры и Лисп как раз и задумывались для работы со сложными проблемами. Искусственное упрощение структур означало бы пренебрежение действительной сложностью проблем.
Идеей Лиспа является попытка упростить решение проблемы, структурируя используемые данные и упрощая программы. Такой подход оказался полезным, например, в объектно-ориентированном программировании и в экспертных системах. Эти области содержат больше количество знаний со сложной структурой, которые интерпретируются часто довольно простыми процедурами поиска и принятия решения.
Структура языка Лисп проста и последовательна. Функции Лиспа можно напечатать в ясно структурированном и хорошо читаемом виде. Во многих системах существует возможность использования формы записи с малым количеством скобок и близкой по виду к более традиционным языкам.
Хорошим доказательством простоты использования Лиспа является его широкое применение в исследовательских работах по программированию сложных методов обработки знаний. Большая часть значимых программ искусственного интеллекта запрограммирована на Лиспе или на основанном на нем языке более высокого уровня. По результатам некоторых исследований можно, скажем, в среде программирования Лисп-машины достичь повышения производительности программирования в несколько десятков раз по сравнению, например, с программированием на Коболе.