|
|
В.ВодолазкийПервые шаги в GNU Common LispВ статье рассматриваются начальные шаги по освоению языка программирования Lisp. Описывается процедура установки среды в системе Linux и приводятся примеры нескольких простых программ. Описание основано на версии GNU Common Lisp. Мифы о языке ЛиспВот несколько наиболее распространенных "страшных историй" с которых я хотел бы начать.
Лисп шаг за шагомНу что же, если вы хотя бы частично согласились с моими доводами, давайте попробуем пощупать Лисп собственными руками. В принципе, практически в любой дистрибутив Linux входит та или иная версия Лиспа, но мы будем вести речь об "официальной версии" проекта GNU, которая по вполне понятным причинам называется GNU Common Lisp (GCL). На момент написания этих строк последней версией на сервере ftp.gnu.org являлась версия 2.4.1. О ней пойдет речь ниже. В состав Slackware 8.0 входит установленная система GCL 2.4.0. Все описываемые примеры вполне работоспособны и в ней, но все равно я рекомендую вам извлечь дистрибутив в котором вы найдете массу полезной информации.Установка GCLПервое, с чего следует начать, это установить Лисп на вашей машине. К слову сказать, если на вашем компьютере установлен дистрибутив Slackware 4.0, то при работе на машинах класса выше Pentium-I GCL работать откажется - скажутся ошибки в устаревших версиях системных библиотек. Так что позаботьтесь об обновлении программного обеспечения. Итак, ваша задача извлечь c с сервера GNU или одного из его зеркал дистрибутив Common Lisp, который находится в файле gcl-2.4.1.tgz , а затем распаковать его (я обычно использую для этой цели каталог /usr/src) и собрать:cd /usr/src tar xvfz gcl-2.4.1.tgz cd gcl-2.4.1 ./configure make make install Все! Система установлена и готова к работе. Никаких перезагрузок не потребуется. Теперь давайте проверим ее работоспособность. Первый вызов ЛиспаДля этого нам нужно вызвать интерпретатор Лиспа с помощью команды gcl . Ну а что делать дальше, показано на рис.1 Рис.1. Первая проба пера в Лиспе Итак, что мы видим... Во-первых, разработчики забыли сменить номер версии, и хотя мы устанавливали GCL 2.4.1 система не признает новый номер версии и по-прежнему выводить старый. Но это опытного программиста пугать не должно. Далее вы можете увидеть как с помощью встроенных функций Лиспа можно решать вычислительные задачи. Несмотря на кажущуюся непривычность записи все достаточно просто. Лет сто назад Владимир Ильич Ленин по какому-то случаю заметил, что весь мир можно описать с помощью дифференциальных функций. Лисп некоторым образом поддерживает этот подход - в нем все также представляется с помощью функций. Любое выражение в Лиспе представляет собой вызов функции, оформленной достаточно стандартным образом, (имя_функции аргумент_1 аргумент_2 ...) В данном случае на рис.1 представлено три стандартные (то есть встроенные) функции - +, - и bye. Последняя функция, хотя и вызывается вообще без аргументов очень важна, поскольку никакими другими способами, за исключением снятия задачи с помощью команды kill вы покинуть Лисп не сможете. Стандартных функций в Лиспе за последние полсотни лет накопилось достаточно много, но знать их все вам не потребуется - Лисп относится к тем языкам, где зачастую проще заново изобрести велосипед, предназначенный для одноразовой поездки, чем изучать неизвестную вам до сих пор стандартную функцию. Создание собственных функцийТеперь давайте попробуем продвинуться чуть дальше. Итак, мы уже знаем, как вызывается функция с аргументами-константами, но ведь нам может потребоваться вычислить значение этих аргументов? Давайте в качестве примера рассчитаем длину гипотенузы прямоугольного треугольника со сторонами 3 и 4. Для этого вам достаточно ввести в gcl команду: (sqrt (+ (*3 3) (* 4 4))) В результате на выходе вы получите число 5.0. Обратите внимание, что Лисп самостоятельно осуществляет приведение типов переменных и вам обычно не нужно преобразовывать тип аргумента или результата к какому-либо конкретному виду. Эта черта Лиспа беззастенчиво воровалась огромным количеством разработчиков и не раз выдавалась за "уникальное достижение", хотя впервые появилась еще 50 лет назад. Ну да ладно, пора двигаться дальше. Давайте попробуем теперь создать собственную функцию, которая позволит нам рассчитывать гипотенузу для любого прямоугольного треугольника. Для этого нам потребуется так называемая специальная форма - конструкция Лиспа, обеспечивающая генерацию исходного кода программы. Впрочем, для программиста эта форма ничем не отличается от функции. Имя этоф формы defun и общий ее вид следующий, (defun ( <список_аргументов>) <тело-функции> )Понятно, что список аргументов может быть пустым, как например, у функции bye. Что касается тела функции, то это последовательность вызова функций Лиспа, которые реализуют логику работы создаваемой нами функции. Любая функция возвращает результат - это значение, получаемое в результате вычисления последней функции в теле. Фуу-ф, понятие "функция" настолько часто встречается в Лиспе, что избежать тавтологии кажется совершенно невозможным... Но ближе к делу, чтобы продемонстрировать еще пару моментов, мы поступим следующим образом. Открывайте любой текстовый редактор и вводите определения, представленные на рис.2. Рис.2. Программа расчета сторон прямоугольного треугольника Прежде чем приступить к запуску этой программы на выполнение, давайте отметим несколько новых понятий. Во-первых, точка с запятой является идентификатором текстового комментария. При этом в Лиспе существует традиция, по которой тремя точками с запятой отделяются комментарии, имеющие отношение ко всей программе в целом, двумя - к отдельной функции или фрагменту кода, а одна точка с запятой ставится в конце строки, если комментарий имеет локальный характер и относится только к этой отдельной строке. Второе, на что необходимо обратить внимание - это определение функций. Я глубоко убежден, что писать тексты программ по-русски все же удобнее, чем по-английски, особенно, если оценки за "аглицкий" в школе выше тройки поднимались редко. И мое субъективное мнение разделяют французы (книги по Лиспу на французском используют примеры, написанные на французском) и даже финны, которых не больше, чем жителей в Московской области. А то, что мы используем кириллицу, только играет нам на руку - вы сразу видите, работаете ли вы со встроенной функцией, или со своей собственной. Сами определения функций имеют вполне обыденный вид. В принципе можно было бы обойтись и одной функцией гипотенуза, но введение дополнительной функции квадрат упрощает понимание всей программы, и позволяет предотвратить дублирование ввода одного и того же кода. Это как раз и является предпосылкой для сокращения количества вложенных скобок в Лиспе - старайтесь всегда, когда есть возможность, выносить определения отдельных фрагментов в специализированные функции. Теперь обратите внимание на функцию setq. Не вдаваясь в детали отметим, что это функция, предназначенная для присвоения значения переменной, которая является первым аргументом. Значение определяется вторым аргументом, которым может быть и вызов функции. Собственно говоря, и имя самой переменной, которой присваивается значение, также может быть получено в результате вызова функции, но я вас пока такими примерами пугать не буду. И наконец, завершают программу несколько вызовов функций для вывода результатов расчетов. В данном случае нам достаточно трех из них. Функция prin1 выводит на печать результат оценки значения своего первого аргумента, а функция princ интерпретирует свой аргумент как символьную строку. И наконец, terpri предназначена для перехода на новую строку. Конечно, в Лиспе имеются и более мощные, унифицированные средства форматирования входных и выходных потоков, но я пока не буду забивать вам голову этими материями... Итак, с программой вроде бы разобрались. Предположим, вы поместили ее в файл gipotenuza.lsp. Теперь нам осталось запустить ее на выполнение. Сделать это можно двумя способами. Во-первых, вы можете вызвать gcl из командной строки, передав ему в качестве аргумента входной файл: gcl -f gipotenuza.lsp Файл будет загружен и автоматически запущен на выполнение. А поскольку последним вызовом функции в этом файле является команда завершения работы Common Lisp, на консоль будет выведена строка, Треугольник имеет стороны: 12 5 13.0 Второй способ предполагает загрузку программы из самого Common Lisp. Для этого вы используете вызов функции load, которой в качестве аргумента передается имя файла. Рис.3. Вызов программ на Лиспе из командной строки и из среды. Все определения функций и их вызовы при любом способе вызова последовательно обрабатываются и выполняются. Конечно, если бы мы не ввели завершающий вызов bye, то после завершения обработки программы мы остались бы в среде GCL. Но это правильно! Ведь на может потребоваться загрузить не только готовую программу, но и скажем, файл, в котором содержатся определения наших функций. Работа с графическим интерфейсомНу что же, теперь пришло время познакомить читателя с использованием графического интерфейса. Одна из особенностей GCL заключается в том, что эта система позволяет достаточно просто подключать внешние библиотеки, входящие в состав Linux. Одной из таких библиотек является Tk - это бибилотека для быстрого проектирования графического интерфейса пользователя. Во время компиляции вашей Лисп-системы эта библиотека была подключена к интерпретатору Лиспа и нам остается ее только активизировать.Конечно, чтобы детально понять, как работает приведенный ниже пример, вам потребуется гораздо больше информации, чем была приведена ниже. Но чтобы не скрывать лес за деревьями, я не буду размениваться на частности. В конце концов вы можете принимать пока приводимые конструкции как некоторые "заклинания" суть которых станет ясна позднее. Ключевая конструкция для подключения графического интерфейса выглядит очень просто - это один единственный вызов функции (si::tkconnect) который приводит к открытию пустого окошка в среде X-Window (понятно, что запускать программу, ориетнированную на работу в "Иксах" вы должны при загруженном Х-сервере). Теперь давайте подойдем к проблеме радикально - создадим аналог "Hello, World", ориентированный на работу в Tk-интерфейсе и поместим эту программу в файл msg.lsp. Вот ее текст: ;;; ;;; msg.lsp - Программа вывода текстового окошка с испльзованием ;;; встроенного шлюза Lisp-Tk ;;; (si::tkconnect) ; подключаем библиотеку Tk (in-package "TK") ; включаем ее каталог в наше пространство симоволов ;; Функция wm предназначена для настройки параметров оконного ;; менеджера. Данный вызов позволяет изменить заголовок окна (wm :title '|.|' "Всем привет от GCL 2.4.1") ;; Теперь создаем два фрейма в окошке - верхний для текстовых ;; сообщений, а нижний - для кнопок (frame '.main :relief "raised" :borderwidth 0) (pack '.main :side "top") (message '.main.txt :relief "raised" :borderwidth 1 :width 600 :text " Наша первая программа на Common Lisp! Предназначена для вывода в окошке списка файлов из корневого каталога системы " ) (frame '.buttons :relief "raised" :borderwidth 1) (pack '.buttons :side "bottom") ;; Помещаем фреймы в основное окно с использованием менеджера геометрии, ;; заимствованного из Tk в Java (pack '.main.txt :side "top" :fill "x") ;; Теперь нам необходимо создать кнопочку, помещаемую в нижний кадр и ;; связать с ней некоторую операцию (button '.buttons.ls :text "О программе" :command `(about)) (button '.buttons.ok :text "Закончить" :command `(bye)) (pack '.buttons.ls '.buttons.ok :side "left" :expand 1) (pack '.buttons :side "bottom" :expand "yes" :fill "both") (focus '|.|) ;; about - Новое сообщение (defun about() (destroy '.main.txt) (message '.main.txt :text "Мы легко можем заменить текущее сообщение в окошке программы на новое. А чтобы завершить работу достаточно нажать кнопку \"Закончить\"" :width 600) (pack '.main.txt) )В целом построение программы на Lisp/Tk не слишком отличается от проектирования графического интерфейса на языке Java. В конце концов, и Tk, и Java родились в лабораториях фирмы Sun и используют один и тот же механизм описания геометрии экранных элементов. Поэтому необходимо только указать на общую последовательность действий. Вначале мы создаем корневое окно, которое имеет зарезервированное имя ., хотя и записывается в виде |.| - это просто одна из маленьких хитростей на которых я не буду пока фиксировать ваше внимание. Затем мы начинаем вводить сегменты экрана - фреймы, которые не слишком отличаются от фреймов, используемых в HTML-файлах. А уже затем, в каждый фрейм мы помещаем тот или иной экранный элемент. В нашем случае это текстовое сообщение (обратите внимание на сохранение всех элементов форматирования строк) Во втором, нижнем фрейме мы помещаем две кнопочки. И снова менеджер геометрии самостоятельно размещает их в пределах своей ответственности. Нам необходимо только задать имя кнопки и определить команду, которая будет выполняться при нажатии на нее. А поскольку Лисп - это постоянный вызов функций, то вполне логично связать с кнопкой либо свою собственную, либо системную функцию. Вот как выглядит результат работы нашей программы: Как видно из приведенных рисунков, при корректно настроенной системной локали и установленных кириллических шрифтах вы вполне можете обойтись без принудительного ввода описаний шрифтов. Впрочем видно также, что менеджер геометрии самостоятельно определяет размер окон, что не всегда желательно... Но в этом ничего страшного нет, вскоре вы узнаете, как загнать своевольный Tk в прокрустово ложе требований программиста. А пока что попробуйте "пощупать" Лисп самостоятельно - в следующем номере мы продолжим знакомство с основными конструкциями и приемами работы, но собственный опыт еще никому не мешал.
|
Послать письмо voldemarus@narod.ru
|