Сегодня расскажу про такую удобную штуку как 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 пока не выйдите. Вот, что у меня. Картинка кликабельна.