Ответ 1
Идея REPL исходит от сообщества Lisp. Существуют и другие формы текстовых интерактивных интерфейсов, например интерфейс командной строки. Некоторые текстовые интерфейсы также позволяют выполнять подмножество некоторого языка программирования.
REPL означает READ EVAL PRINT LOOP: (цикл (печать (оценка (чтение)))).
Каждая из четырех вышеперечисленных функций является примитивной функцией Lisp.
В Lispе REPL не является интерпретатором командной строки (CLI). READ
не читает команды, а REPL не выполняет команды. READ
читает ввод и преобразует его в данные. Таким образом, функция READ
может читать все виды s-выражений - не только код на Lispе.
READ читает s-выражение. Это формат данных, который также поддерживает кодирование исходного кода. READ возвращает данные Lisp.
EVAL берет исходный код Lisp в виде данных Lisp и оценивает его. Могут возникнуть побочные эффекты, и EVAL возвращает одно или несколько значений. Как реализуется EVAL с помощью интерпретатора или компилятора, не определено. Реализации используют разные стратегии.
PRINT берет данные Lisp и печатает их как s-выражения.
LOOP просто зацикливается на этом. В реальной жизни REPL является более сложным и включает обработку ошибок и подциклы, так называемые разрывные циклы. В случае ошибки можно получить еще один REPL с добавленными командами отладки в контексте ошибки. Значение, полученное за одну итерацию, также можно повторно использовать в качестве входных данных для следующей оценки.
Поскольку в Lisp используются как код-как-данные, так и функциональные элементы, есть небольшие отличия от других языков программирования.
Подобные языки предоставляют также похожие интерактивные интерфейсы. Например, Smalltalk также допускает интерактивное выполнение, но он не использует формат данных для ввода-вывода, как это делает Lisp. То же самое для любого интерактивного интерфейса Ruby/Python/...
Вопрос:
Итак, насколько важна первоначальная идея чтения ВЫРАЖЕНИЙ, их оценки и ПЕЧАТИ их значений? Это важно по отношению к тому, что делает другой язык: чтение текста, его синтаксический анализ, выполнение, печать чего-либо и, при необходимости, печать возвращаемого значения? Часто возвращаемое значение на самом деле не используется.
Таким образом, есть два возможных ответа:
-
Lisp REPL отличается от большинства других текстовых интерактивных интерфейсов, потому что он основан на идее ввода-вывода данных s-выражений и их оценки.
-
REPL - это общий термин, описывающий текстовые интерактивные интерфейсы для реализаций языка программирования или их подмножеств.
В реальных реализациях Lisp REPL имеют сложную реализацию и предоставляют множество сервисов, вплоть до интерактивных презентаций (Symbolics, CLIM, SLIME) объектов ввода и вывода. Расширенные реализации REPL доступны, например, в SLIME (популярной интегрированной среде разработки на Emacs для Common Lisp), LispWorks и Allegro CL.
Пример взаимодействия Lisp REPL:
перечень товаров и цен:
CL-USER 1 > (setf *products* '((shoe (100 euro))
(shirt (20 euro))
(cap (10 euro))))
((SHOE (100 EURO)) (SHIRT (20 EURO)) (CAP (10 EURO)))
Заказ, список товара и количество:
CL-USER 2 > '((3 shoe) (4 cap))
((3 SHOE) (4 CAP))
Цена для заказа, *
является переменной, содержащей последнее значение REPL. Он не содержит это значение в виде строки, но содержит реальные фактические данные.
CL-USER 3 > (loop for (n product) in *
sum (* n (first (second (find product *products*
:key 'first)))))
340
Но вы также можете вычислить код на Lispе:
Давайте возьмем функцию, которая добавляет квадраты двух своих аргументов:
CL-USER 4 > '(defun foo (a b) (+ (* a a) (* b b)))
(DEFUN FOO (A B) (+ (* A A) (* B B)))
Четвертый элемент - это просто арифметическое выражение. *
относится к последнему значению:
CL-USER 5 > (fourth *)
(+ (* A A) (* B B))
Теперь мы добавим код вокруг него, чтобы связать переменные a
и b
с некоторыми числами. Мы используем функцию Lisp LIST
для создания нового списка.
CL-USER 6 > (list 'let '((a 12) (b 10)) *)
(LET ((A 12) (B 10)) (+ (* A A) (* B B)))
Затем мы оцениваем вышеприведенное выражение. Опять же, *
относится к последнему значению.
CL-USER 7 > (eval *)
244
Есть несколько переменных, которые обновляются с каждым взаимодействием REPL
. Примерами являются *
, **
и ***
для предыдущих значений. Также есть +
для предыдущего ввода. Эти переменные имеют в качестве значений не строки, а объекты данных. +
будет содержать последний результат операции чтения REPL. Пример:
Каково значение переменной *print-length*
?
CL-USER 8 > *print-length*
NIL
Давайте посмотрим, как список читается и печатается:
CL-USER 9 > '(1 2 3 4 5)
(1 2 3 4 5)
Теперь давайте установим вышеуказанный символ *print-length*
в 3. ++
относится ко второму предыдущему прочитанному вводу, как к данным. SET
устанавливает значение символов.
CL-USER 10 > (set ++ 3)
3
Тогда вышеприведенный список печатается по-другому. **
относится ко второму предыдущему результату - данные, а не текст.
CL-USER 11 > **
(1 2 3 ...)