picolisp.ru
Переключить темный/светлый/авто режим Переключить темный/светлый/авто режим Переключить темный/светлый/авто режим Back to homepage

Первая программа на PicoLisp

Перевод. Оригинал здесь

Это 11-я статья из цикла Введение в PicoLisp.

В ней мы обсудим соглашения об именовании и создадим, наконец, нашу первую программу на PicoLisp.

Соглашения об именовании

В PicoLisp есть небольшое количество соглашений об именовании различных объектов, призванных избежать конфликтов имён и сделать код более читаемым. Вот некоторые из наиболее важных:

  • Имена глобальных переменных начинаются со звездочки *.
  • Имена глобальных констант записываются прописными.
  • Функции и другие глобальные символы начинаются со строчной буквы.
  • Локальные символы начинаются с прописной буквы (например, локальные переменные в функциях).

Этот список не полон, с полным списком соглашений по написанию кода вы можете ознакомиться здесь.

Что означает "конфликт имён"?

Давайте возьмём для примера функцию PicoLisp car. Поскольку данные и код в PicoLisp - одно и то же, локальная переменная с именем "car" может переопределить встроенную функцию car:

: (de max-speed (car)
   (.. (get car 'speeds) ..) )
-> max-speed

Внутри функции max-speed встроенная в язык функция car переопределена, что безусловно приведёт к сбою при выполнении многих команд, например (car Lst). Поэтому мы должны начинать имена локальных переменных с прописной буквы:

: (de max-speed (Car)  # 'Car' начинается с прописной буквы
   (.. (get Car 'speeds) ..) )
-> max-speed

Другим примером могут быть глобальные символы T и NIL, которые можно переопределить по ошибке:

(de foo (R S T)
   ...

Создание программ

Пользоваться при написани кода только REPL - занятие не из лёгких. Сейчас мы создадим небольшую программу, которая выполняет всего одну функцию - запрашивает имя пользователя и выводит его на экран. В этом примере мы покажем, как должна выглядеть типичная программа на PicoLisp.


Для лучшего усвоения материала пишите код прямо во время чтения статьи. Если такой возможности у вас нет - в конце вы найдете ссылку на файл с исходным кодом.


Как говорилось в серии статей "60 функций PicoLisp", для того, чтобы прочитать строку из входного потока, можно воспользоваться функцией line. Давайте присвоим этой строке имя Name. Воспользуемся REPL для проверки нашей идеи:

: (setq Name (line))
Mia
-> ("M" "i" "a")

Давайте проверим, что имя присвоилось:

: (prinl "Hello " Name)
Hello Mia

Кажется, всё работает, как предполагалось. Давайте теперь создадим текстовый файл с названием greeting.l (расширение .l используется для файлов с программами на PicoLisp) и вставим туда следующий текст:

# greeting.l

(prinl "Hello! Who are you?")
(setq Name (line))
(prinl "Hello " Name "!")

Сохраните файл. Теперь вы можете запустить программу командой pil greeting.l из консоли. К сожалению, результат выглядит несколько странным:

$ pil greeting.l
Hello! Who are you?                                                              
Hello !

(знак $ - это приглашение командной оболочки, его вводить не надо!)

Кажется, команда (line) пропускается. Почему так происходит?

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

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

# greeting.l

(prinl "Hello! Who are you?")
(setq name (in NIL (line)))
(prinl "Hello " name "!")

Раотает! Однако после выполнения кода не происходит выход из программы. Чтобы программа завершалась после выполнения инструкций, добавьте в конец строку (bye).


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

(de greeting (Name)
   (prinl "Hello " Name "!") )

Обратите внимание, что по соглашению о написании кода "Name" написана с прописной буквы. Также обратите внимание, что в конце второй строки стоит пробел между скобками: ) ). Он показывает, что последняя скобка закрывает выражение, которое началось не на текущей строке, а на предыдущей. Это стандартный подход в языках семейства Lisp.

Конечно же, определение функции greeting должно происходить раньше её использования, чтобы интерпретатор знал, о какой функции идёт речь. Текст программы после изменения выглядит так:

# запустите эту программу командой "pil greeting.l"

(de greeting (Name)
    (prinl "Hello " Name "!") )

(prinl "Hello! Who are you?")
(setq Name (in NIL (line)))
(greeting Name)
(bye)

Готовый файл с программо вы можете скачать отсюда.

Создание исполняемых файлов

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

Используется механизм так называемых "интерпретируемых файлов". Если первые два символа файла - #!, то операционная система откроет файл с помощью программы-интерпретатора, путь к которой указан в первой строке. Опционально можно передать интерпретатору аргументы.


Шаг 1. Найдите путь до интерпретатора

Если вы устанавливали PicoLisp глобально, например, используя менеджер пакетов вашего дистрибутива, то почти наверняка исполняемый файл picolisp находится в каталоге /usr/bin. Убедиться в этом можно, выполнив команду which:

$ which picolisp
/usr/bin/picolisp

Если вы выполняли локальную установку, то файл инрерпретатора picolisp находится в подкаталоге bin/ того каталога, куда вы устанавливали PicoLisp: <Path to Folder>{=html}/pil21/bin. Найдите полный путь до файла с помощью команды locate:

$ locate "bin/picolisp"
/home/user/pil21/bin/picolisp

Если команда locate выдаёт ошибку, значит вам нужно установить пакет mlocate или plocate через ваш пакетный менеджер и запустить обновление базы данных для этой команды: $ sudo updatedb.

Шаг 2. Добавление библиотечного файла lib.l

Одного интерпретатора недостаточно. Нам понадобится ещё библиотечный файл, который называется lib.l. При глобальной установке этот файл обычно находится в каталоге /usr/lib/picolisp:

$ locate "picolisp/lib.l"
/usr/lib/picolisp/lib.l

При локальной установке этот файл находится в корне с папкой, куда установлен picolisp:

$ locate "lib.l"
/home/user/pil21/lib.l

Отлично! Теперь вернёмся к файлу с нашей программой.

Шаг 3. Добавляем путь к интерпретатору и библиотеке

Первая строка вашего файла должна выглядеть так:

#! /usr/bin/picolisp /usr/lib/picolisp/lib.l

Замените путь к интерпретатору и библиотеке, если они у вас отличаются. Как вариант, если у вас только локальная установка, вы можете создать ссылки на интерпретатор и библиотеку в стандартных расположениях:

$ ln -s /home/foo/pil21 /usr/lib/picolisp
$ ln -s /usr/lib/picolisp/bin/picolisp /usr/bin

Шаг 4. Делаем файл с программой исполняемым

Последний шаг, который нам остался - сделать файл с программой исполняемым. Это делается командой chmod +x greeting.l. Теперь вы можете запускать программу командой ./greeting.l.

$ ./greeting.l 
Hello World! Who are you? 
Mia
Hello Mia!

Теперь всё должно работать. Готовый файл с программой вы можете загрузить здесь.

Передача аргументов нашей программе

Иногда требуется запустить программу с определёнными параметрами, вместо того, чтобы вводить их после запуска.

Давайте изменим нашу функцию greeting так, чтобы она получала имя из глобальной переменной:

(de greeting ()
  (prinl "Hello " *Name "!"))

В PicoLisp есть встроенная функция opt, которая принимает аргументы из командной строки. Присвоим переменной *Name значение из командной строки следующим кодом:

(setq *Name (opt))

Ваша программа должна выглядеть вот так:

#! /usr/bin/picolisp /usr/lib/picolisp/lib.l

(de greeting ()
  (prinl "Hello " *Name "!"))

(setq *Name (opt))
(greeting)

(bye)

Теперь мы можем передавать имя программе прямо через вызов в командной строке следующим образом:

$ ./greetings.l Mia
Hello Mia!

Работает! Но что будет, если мы вызовем программу без аргументов?

$ ./greetings.l
Hello !

Было бы неплохо, если бы программа проверяла, передано ли ей имя через аргумент командной строки и предлагала ввести его, если это забыли сделать. Используем для этого управляющую конструкцию ifn ("если не"):

(ifn *Name
  (prinl "Пожалуйста, введите своё имя!")
  (greeting) )

Давайте проверим, как работает программа теперь:

$ ./greeting-with-args.l 
Please specify a name!

$ ./greeting-with-args.l Mia
Hello Mia!

Полный код программы вы можете скачать по ссылке.


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