Чтение-вычисление-вывод

Сегодня расскажу про такую удобную штуку как REPL  — цикл чтение-вычисление-вывод. Этот режим полезен для разработки кода и его отладки на-лету. Т.е. пишете код и тут же его запускаете. Этот режим может быть знаком вам по математическим пакетам или js консоли в браузере или python интерпретатора. Однако, появился такой режим в лиспе.

Собственно, когда вы запускаете slime через emacs, то входите как раз в REPL. Если всё установилось правильно, то даже строка с приглашением для ввода будет выглядеть так:

REPL>_

Набирая команду и нажимая enter мы передаём её на выполнение интерпретатору. Что там дальше происходит в деталях не скажу, но если упростить, то интерпретатор её парсит (в случае лиспа парсить практически ничего не нужно, ну может макросы раскрыть) и выполняет. И так строчка за строчкой. Один из вариантов использования такого режима это вычисления. Поскольку лисп поддерживает вычисления с рациональными числами (т.е. дробями), то можно считать без потери точности. Второй плюс — поскольку лисп функциональный, то векторные операции здесь должны работать почти так же, как и скалярные. Выполним такой незамысловатый код:

(defparameter *a* 1/3)
(defparameter *b* 2/3)
(format t "~a + ~a = ~a~&" *a* *b* (+ *a* *b*))

На выходе получим нечто вроде 1/3 + 2/3 = 1, никакого округления и потери точности. По-моему тот, кто это придумал был довольно прозорлив. Теперь тоже самое, но со списком

(defparameter *list-a* '(1/3 1/6 1/9))
(defparameter *list-b* '(2/3 3/6 5/9))
(format t "~{~a~^, ~}~&" (map 'list #'+ *list-a* *list-b*))

Не сказать, что просто, но всё же в одну строчку, без циклов. Теперь немного расскажу о том, что происходит на этих примерах:

В первом примере (как и во втором) первые две строчки это объявление переменных. В лиспе вроде это не называется глобальным, но аналогия с глобальными переменными других языков несомненно есть. Отличие defparameter и defvar (который будет потом) в том, что если переменная уже объявлена, то первый изменит её значение, а второй нет. Дальше идёт операция сложения двух скаляров или двух списков и вывод этого в консоль. Да format может выводить и списки без циклов одной строкой. О возможностях format я напишу в следующих статьях, да и про map тоже. Сейчас кратко — первый параметр что обрабатываем — списки. Второй какая функция, можно было лямбду вставить или использовать стандартную, например сложение. А дальше идут списки которые мы обрабатываем. Другими словами вот так тоже сработает:

(format t "~{~a~^, ~}~&" (map 'list #'+ *list-a* *list-b* *list-a* *list-a*))
(format t "~{~a~^, ~}~&" (map 'list #'* *list-a* *list-b*))
(format t "~{~a~^, ~}~&" (map 'list #'/ *list-b* *list-a*))

Вот такой режим чтение-вычисление-вывод. Если вывалились в отладчик, то нажимайте q пока не выйдите. Вот, что у меня. Картинка кликабельна.

%d1%81%d0%bd%d0%b8%d0%bc%d0%be%d0%ba

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.