Введение в программирование на Лиспе. Учебное пособие
.pdf
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
(setq x 'y) |
|
(set x 'NEW) |
|
(print x) |
|
(print y) |
|
Пример 11.2. Побочный эффект присваиваний с вычисляемой левой частью
Напечатается Y и NEW.
Циклы
Работа с циклами обеспечена в Лиспе достаточно традиционно.
(loop <форма>...)
Базовая форма цикла, представляющая собой встроенную функцию, многократно вычисляющую свои аргументы – тело цикла – до тех пор, пока на будет выполнен какой-либо явный выход из цикла, такой как
RETURN.
(do(<параметры>...)(<предикат > < результат >...) < форма >...)
(do*(<параметры >...)(<предикат > < результат >...) < форма >...)
Обобщенные формы цикла, отличающиеся правилом связывания параметров цикла – независимо и последовательно.
(dolist (<переменная > < список > [<результат >] ) < форма >...)
Цикл, перебирающий список выражений, поочередно присваиваемых переменной цикла.
(dotimes (<переменная > < число > [<результат >] ) < форма >...)
Цикл, работающий заданное число шагов от 0 до N-1
121
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
< параметры > задаются как списки вида
(<переменная> <начальное_значение> [<шаг>] )
вкотором:
<переменная > - символ с исходным значением Nil.
<начальное_значение > - начальное значение параметра.
<шаг > - выражение для вычисления параметра на каждом шаге цик <предикат> - ограничитель цикла
<результат> - результирующее выражение (при отсутствии - NIL) <форма> - тело цикла, работает как неявная форма prog.
Значение дает последнее результирующее выражение.
Примеры программ с циклами
(defun first-a (la)
;; самый левый атом
(setq x la) (loop
(setq x (car x)) ; левый элемент структуры
(cond ((atom x)(return x)) )
; явный выход из цикла при обнаружении атома
) )
(print (first-a '(((123) 46) 5) ))
Пример 11.3. Выбор самого левого атома из произвольной структуры данных
(defun len-do (ld) ;; длина списка
(do
((x ld (cdr x)); на каждом шаге переход к хвосту списка (N 0 (1+ N))) ; подсчет числа шагов
((null x) N)) |
; выход из цикла при пустом списке |
) |
|
(print (len-do '(1 2 3 4 5)))
Пример 11.4. Вычисление длины списка
122
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
(defun list-pa (lp) (setq rl nil)
(dolist
(el lp rl) ; параметры перебора и результат
(setq rl (cons (cons el el) rl))
))
(print (list-pa '(a b c d))) ; = ((A . A)(B . B)(C . C)(D . D))
Пример 11.5.
(defun ind-n (ln n) (setq bl ln) (setq ind nil) (dotimes (i n ind)
(setq ind (cons (- n i) (cons (car bl ) ind )))
(setq bl (cdr bl))
))
(print (ind-n '(a b c d e f g) 4)) ; = D
Пример 11.6. Индексный выбор элемента из списка
Таблица 11.1. Clisp: Функции, моделирующие императивный стиль
|
(Go Атом ) |
|
Безусловный переход на оператор, помеченный |
|
|
|
|
Атомом |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
(Prog Атомы-или- |
|
Вычисляет последовательность форм в |
|
|
|
Формы …) |
|
императивном стиле |
|
|
|
|
|
|
|
|
|
(Prog1 Форма …) |
|
Вычисляет формы, формальный результат – |
|
|
|
|
значение первой из них. |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
(Prog2 Форма …) |
|
Вычисляет формы, формальный результат – |
|
|
|
|
значение второй из них. |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
(Progn Форма …) |
|
Вычисляет формы, формальный результат – |
|
|
|
|
значение последней из них. |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
(Return Форма ) |
|
Результат и завершение формы Prog |
|
|
|
|
|
|
||
|
(Do (var ... |
) ( expr rez |
|
Цикл с последовательным заданием параметров и |
|
|
...) expr ... |
) |
|
выходом по заданному условию |
|
|
|
|
|
|
|
|
(Do* (var |
...) ( expr |
|
Цикл с параллельным заданием параметров и |
|
|
rez ...) expr ...) |
|
выходом по заданному условию |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123
Л.В. Городняя, Н.А. Березин |
|
Введение в программирование на Лиспе |
||
|
(Dolist (var list [rez] ) |
|
Цикл перебора значений параметра из списка |
|
|
expr ...) |
|
|
|
|
|
|
|
|
|
(Dotimes (var number |
|
Цикл, работающий заданное число раз |
|
|
[rez] ) expr ...) |
|
|
|
|
|
|
|
|
|
(Loop expr ...) |
|
Цикл работающий до внутреннего Return |
|
Выводы:
При реализации языка программирования происходит его расширение с целью повышения практичности программирования.
Стандартные, операторно-процедурные построения моделируются с помощью функций.
1) Применение prog-выражений позволяет писать паскалеподобные программы, состоящие из операторов, предназначенных для исполнения. (Точнее "алголоподобные", т.к. появились лет за десять до паскаля. Но теперь более известен паскаль.)
124
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
Расширения и приложения Лиспа
В заключении рассмотрим как дальше осваивать Лисп, практично использовать возможности Лисп-систем и систем функционального программирования. Познакомимся немного с историей Лиспа
На примере GNU Clisp ( Common Lisp language interpreter and compiler)
рассмотрим типичные возможности Лисп-систем, описанные в manфайле комплекта поставки [GNU Clisp]1)
Вызов Лисп-интерпретатора и/или компилятора.
Без аргументов (опций) выполняется цикл "чтение-вычисление-печать", при котором выражения читаются поочередно из стандартного потока ввода, вычисляются Лисп-интепретатором, и полученные результаты выводятся в стандартный поток вывода. Опция –c специфицирует Лисп-файлы, предназначенные для компиляции в байт-код, который выполняется более эффективно.
Формат вызова Лисп-системы:
clisp [ -h ] [ -m memsize ] [ -M memfile ] [ -L language ] [ -N directory ] [ -q ] [ -I ]
[ -i initfile ... ] |
[ -c [ -l ] lispfile |
[ -o outputfile ] ... |
] [ -p packagename ] [ -x expression ]2) |
OPTIONS
-h Показывает формат вызова Лисп-системы
-m memsize Установливает объем памяти. Для современных версий игнорируется.
-M memfile Определяет внутреннее наполнение памяти Лиспсистемы. Оно должно быть создано функцией "saveinitmem".
-L language Задает язык сообщений для взаимодействия с пользователем. (английский, немецкий, французский и др.) Влияет на тексты диагностики.
125
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
-N directory Указывает, где искать файлы с текстами сообщений.
-q Ни заставки, ни прощального текста не выдается.
-I вариант диалога в стиле ILISP (популярный интерфейс, принятый в редакторе Emacs)
-i initfile ... Специфицирует инициализирующие файлы, которые будут загружены при запуске системы. Это должны быть исходные или компилированные Лисп-файлы,
-c lispfile ... Компилирует специфицированные Лисп-файлы в байт-код. Компилированные файлы затем загружаются вместо исходных, чтобы повысить эффективность.
-o outputfile Задает файл вывода или директорию для компиляции предшествующего лисп-файла.
-l будет выполняться листинг байткода для компилируемых файлов. Полезно только для целей отладки.
-p packagename При загрузке устанавливает начальное значение переменной *package*
-x expressions Выполняет серию произвольных выражений вместо цикла "read-eval-print". Значения выражений выводятся в стандартный поток вывода. Согласно системным соглашениям выражения должны быть заключены в скобки, а двойным кавычкам и обратным чертам следует предпослать обратную черту.
@optionfile Подставляет содержимое файла как аргумент для запуска Лисп-системы. Каждая строка воспринимается как отдельный аргумент.
При работе с Лисп-системой полезны следующие возможности:
(apropos name) перечисляет символы, включающеие " name "
(exit) or (quit) or (bye) - выход из Лисп-системы
126
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
EOF (Ctrl-Z) Покидает текущий " read-eval-print " цикл
Стрелки управления курсором позволяют построчно редактировать и просматривать историю ввода текста программы.
Соглашение об именах файлов:
lisp.exe основной исполнитель
lispinit.mem исходный/начальный образ/состояние памяти
config.lsp конфигурация и настройки
*.lsp исходные тексты на Лиспе
*.fas результат компиляции – байт-код
*.lib библиотечная информация, создаваемая и используемая компилятором
*.c Си-код, компилированный по исходному Лисп-тексту
Практичные расширения Лиспа
Средства и методы программирования на Лиспе образуют два слоя. Глубинный слой - локальное программирование, нацеленное на определение:
строгих функций, безотходных структур данных, регулярных отображений,
методов оперирования вычислениями.
Внешний слой - моделирование практичных парадигм программирования и механизмов их реализации:
прототипы и макеты программ, интеграция разных стилей и методов программирования,
учебное и экспериментальное программирование,
127
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
проверка новых идей и подходов к организации информационных систем.
Естественно, работа на внешнем слое требует своей терминологии и развития понятий, отражающего расширение класса решаемых задач, повышение уровня общности и организованности решений:
Реальные Лисп-системы обеспечивают полный спектр средств работы с числами с особым вниманием к повышенной точности вычислений и длине представления числа. Средства обработки структур данных обычно позволяют работать с векторами, строками, массивами, хэштаблицами, деревьями, последовательностями и файлами. Имеется работа с мульти-значениями, удобная при моделировании параллельных вычислений.
Строение Лисп-системы формируется как взаимодействие интерпретатора и компилятора, что позволяет гибко сочетать достоинства того и другого подходов к обработке программ. Для нужд компиляции программа дополняется спецификациями типов данных и декларациями, указывающими направление наследования определений. Имеются средства подготовки и использования встроенной документации и системной информации относительно фактического контекста вычислений. Обстановка функционирования системы регулируется механизмом пакетов, в составе которых хранятся различные варианты определений символов, включаемых в создаваемый комплект.
Так, например, пакет CLOS (Common Lisp Object System) поддерживает ООП в терминах классов, методов, суперклассов, экземпляров и семейств функций, подчиненных механизмам инкапсуляции и наследования с управляемым полиморфизмом.
Функциональное программирования
Стиль разработки программ на Лиспе получил название "функциональное программирование" (ФП). Основные положения этого стиля восприняты многими языками программирования с общей логикой уточнения решаемых задач и обобщения решений на основе выбранных специально базовых конструкций:
128
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
1.Базовые конструкции определяются как строгие функции.
2.При необходимости выполняются преобразования программ, (компиляция, оптимизация, ре-факторинг и т.п.) для улучшения эксплуатационных характеристик, связанных с процессами исполнения программ.
3.Важный критерий качества программирования - полнота системы решений и универсальность реализованных определений для синтаксически управляемой обработки данных функциями высоких порядков (компилятор и т.п.), что существенно повышает надежность проектов для развивающихся постановок задач.
4.Разработка ИС предусматривает выполнение ряда шагов, начальные из которых выполняют роль упрощенных прототипов для реализации последующих, возможно другими, более эффективными, средствами.
Отправляясь от однозначных функций, в Lisp-е обеспечено предельно широкое толкование понятия "значение", объединяющее понятия "структура данных" и "функция":
1.Ориентируясь на рекурсивные определения функций, введена схема, достаточно удобная для построения формул, задающих функциональные определения. В качестве примера предложен идеальный Лисп (Pure Lisp).
2.Представления функций отображены на множество списков и атомов и определена универсальная функция, по списочному представлению функции и ее аргументов строящая ее результат.
3.Изучено расширение функционального языка, достаточное для стандартного программирования, естественного для привычных задач.
Конструирование функций средствами чистого Лиспа доставляет интеллектуальное удовольствие, оно сродни решению математических головоломок. В этом исключительно мощном языке не только реализованы основные средства, обеспечившие практичность и результативность функционального программирования, но и впервые опробован целый ряд поразительно точных построений, ценных как концептуально, так и методически и конструктивно, понимание и осмысление которых слишком отстает от практики применения. Понятийно-функциональный потенциал языка Lisp 1.5 в значительной
129
Л.В. Городняя, Н.А. Березин |
Введение в программирование на Лиспе |
мере унаследован стандартом Common Lisp, но не все идеи получили достойное развитие. Возможно это дело будущего - для нового поколения системных программистов, но это уже другая история.
По мере накопления опыта реализации Лиспа и других языков сформированы обширные библиотеки функций, весьма эффективно поддерживающих обработку основных структур данных - списков, векторов, множеств, хэш-таблиц, а также строк, файлов, директорий, гипертекстов, изображений. Существенно повысилась результативность системных решений в области работы с памятью, компиляцией, манипулирования пакетами функций и классами объектов. Все это доступно в современных системах, таких как GNU Clisp, Python, CMUCL и др., основная проблема при изучении которых – слишком много всего, разбегаются глаза, трудно выбрать первоочередное. Все это превращает любой диалект Лиспа в практичный инструментарий, обладающий интересными перспективами.
Результативность идей Лиспа подтверждена самой историей развития его диалектов и родственных ему языков программирования. (Pure Lisp, Lisp 1.5, Lisp 2, Interlisp, CommonLisp, MicroLisp, MuLisp, Sail, Hope, Miranda, Scheem, ML, GNU Clisp, CLOS, Emacs, Elisp, xLisp, Vlisp, AutoLisp, Haskell, Python, CMUCL). Стандарт Common Lisp в сравнении с Лиспом от МакКарти имеет ряд отличий, несколько влияющих на программотехнику. GNU Clisp, xLisp, CMUCL соответствуют стандарту
Common Lisp.
Продуманность и методическая обоснованность первых реализаций Лиспа позволила быстро накопить опыт решения новых задач, подготовить их для прикладного и теоретического программирования. В настоящее время существуют сотни диалектов Lisp-а и языков функционального программирования на базе Lisp-а, ориентированных на разные классы задач и виды технических средств.
Идеи Лиспа выдержали многолетнюю шлифовку и заслужили достойную оценку специалистов и любителей. Универсальность Лиспа достаточна для моделирования любого стиля программирования. Выразительная сила Лиспа обретает новое дыхание на каждом эволюционном витке развития информационных технологий. Потенциал Лиспа нам еще предстоит раскрыть. Стилистика Лиспа
130
