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

60 функций PicoLisp. Часть 3: Ввод и вывод данных

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

Что же, давайте продолжим наше погружение в PicoLisp! Это шестая часть цикла Введение в PicoLisp и третья часть серии "60 функций PicoLisp".

Сегодня мы обсудим функции ввода и вывода данных и как вывести на экран сакраментальную фразу "Привет, Мир!", которую вы, наверное, уже заждались,

Все примеры могут быть выполнены в REPL PicoLisp. Наберите в своей командной строке pil +, чтобы открыть оболочку. В начале этой статьи вы найдете несколько базовых команд для работы с ней.


Печать

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

Давайте начнём с низкоуровневого вывода данных: функция prin выводит все переданные ей аргументы, не вставляя между ними пробелов и не добавляя в конце символа окончания строки. prinl делает то же самое, но в конце вывода добавляет знак окончания строки. Буква "l" в названии означает "line", "строка".

Давайте вызовем каждую функцию дважды, чтобы увидеть разницу:

: (prin "Hello World") (prin ( 1 "abc" (d e (f)) 999 ghi))
Hello World1abcdef999ghi

: (prinl "Hello World") (prinl (1 "abc" (d e (f)) 999 ghi))
Hello World
1abcdef999ghi

Как видите, элементы списка (1 "abc" (d e (f)) 999 ghi)) выведены без скобок и пробелов: 1abcdef999ghi. Внутренняя структура списка потеряна. Глядя на вывод, невозможно определить, где оканчивается один элемент и начинается следующий, шли элементы списка друг за другом или были друг в друга вложены.

Если мы хотим вывести на экран S-выражение полностью, то нам придётся использовать функцию print и её варианты: если в конце вывода нужен пробел, то используйте printsp, если знак окончания строки - то println.

Обратите внимание на различия:

: (print "Hello World") (print (1 "abc" (d e (f)) 999 ghi))
"Hello World"(1 "abc" (d e (f)) 999 ghi)

: (printsp "Hello World") (printsp (1 "abc" (d e (f)) 999 ghi))
"Hello World" (1 "abc" (d e (f)) 999 ghi)

: (println "Hello World") (println (1 "abc" (d e (f)) 999 ghi))
"Hello World"
(1 "abc" (d e (f)) 999 ghi)

Теперь структура списка сохраняется при выводе на экран.


Чтение данных из консоли и файлов

Функция для чтения данных называется read. В соответствии с документацией она читает данные из текущего входного потока, но что же означают эти слова, "текущий входной поток"?

Если вы используете REPL, то текущим входным потоком является консоль - тот экран или то окно терминала, в котором вы запустили команду pil +. Если вы запускаете файл с программой на языке picolisp, то текущим входным потоком является этот файл. Поэтому, если мы хотим создать исполняемый файл, который будет читать данные из консоли, нам нужно изменить входной поток.

Мы вернёмся к этому вопросу позже, когда будем обсуждать создание скриптов, а пока - продолжим наши упражнения в REPL.


Посмотрите на пример работы функции read:

: (list (read) (read) (read))
123
abcd
hello
-> (123 abcd hello)

: (list (read) (read) (read))
A B C
-> (A B C)

Как видите, мы берём три параметра из консоли и объединяем их в список с помощью функции list. При этом пробелы и переход на новую строку распознаются как разделители параметров.

Чтобы прочитать одиночный символ, используйте функцию char:

: (char)                   # Читаем символ из консоли
A                          # (введён символ 'A' и пробел/перевод строки)
-> "A"

Чтобы прочитать строку, используйте line. Вы можете передать в функцию дополнительный флаг NIL или T. Если передан NIL или функция вызвана без флага, то результатом её выполнения будет список из отдельных символов, считанных из строки. Если же передан флаг T, то функция возвратит сплошную строку:

: (line)             # то же самое, что и (line NIL)
abcdefghijkl
-> ("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l")

: (line T)
abcdefghijkl
-> "abcdefghijkl"

Кроме флага, в функцию line можно передать ещё одно или несколько чисел, которые заставят функцию группировать полученные символы. Если используется флаг NIL, то группировка будет происходить в подсписки, если T - в подстроки:

: (line NIL 1 2 3)
abcdefghijkl
-> (("a") ("b" "c") ("d" "e" "f") "g" "h" "i" "j" "k" "l")

: (line T 1 2 3)
abcdefghijkl
-> ("a" "bc" "def" "g" "h" "i" "j" "k" "l")

Давайте теперь опробуем чтение в REPL из файла с помощью функции read. Создайте файл "input.txt" со следующим текстом:

hello world
this is a test
1234567890

Теперь мы изменим входной поток с помощью функции in и повторим наш пример с чтением параметров:

: (in "input.txt"
     (list (read)(read)(read)(read)))
-> (hello world this is)

Мы получили первые четыре слова из файла. Как и в случае с консолью, пробелы и перенос строки разделяют считываемые параметры.


Запись в файл

Точно так же, как in читает из файла, out пишет в файл (то есть перенаправляет выходной поток):

: (out "output.txt" (println 123 '(a b c) 'def))
-> def

В каталоге, из которого мы запускали REPL, появился файл "output.txt". Давайте проверим его содержимое:

$ cat output.txt
123 (a b c) def

Работает!


Внешние библиотеки

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

Для того, чтобы загрузить библиотечный файл, используется функция load:

(load "@lib/http.l" "@lib/xhtml.l")

Знак @ в пути к файлу означает относительный путь к каталогу, куда установлен picolisp.

Давайте взглянем для примера на файл math.l:

math_l

Мы видим вызов setq для переменных pi и pi/2. Давайте загрузим файл math.l в REPL и проверим, доступны ли нам эти переменные:

: pi     # pi не определена
-> NIL

: (load "@lib/math.l")
-> atan2
: pi      # pi определена
-> 3141593

Работает!

Вы наверное ждали увидеть в качестве значения pi 3.14159, а вовсе не 314159? Причина такого поведения в том, что picolisp производит все вычисления над числами с фиксированной точкой, об этом говорилось в статье об арифметике. Позже мы ещё вернёмся к этой особенности языка.


Мы ознакомились с основами ввода и вывода в PicoLisp. В следующих статьях мы рассмотрим условные операторы и циклы.