Функция значений в Common Lisp
Является ли функция значений в Common Lisp просто синтаксическим сахаром для упаковки нескольких значений в список, который деструктурируется вызывающей стороной? Я спрашиваю, потому что я думал, что Common Lisp поддерживает "истинное" возвращение нескольких значений, а не возвращает кортеж или список, как в других языках, таких как python. Кто-то только что сказал мне, что это просто синтаксический сахар, поэтому я хотел бы, чтобы кто-то любезно объяснил это. Чтобы попытаться понять тип, возвращаемый функцией values, я набрал (type-of (values 1 2 3))
и вывод был BIT
, Я искал в справочнике Common Lisp это и не смог найти упомянутое в разделе типов данных. Кроме того, кто-нибудь может поделиться некоторыми ресурсами, которые предлагают, как функция значений реализована в Common Lisp?. Спасибо.
3 ответа
Несколько значений в CL
Язык Common lisp описан в стандарте ANSI INCITS 226-1994 (R2004) и имеет много реализаций. Каждое из них может реализовать несколько значений так, как считает нужным, и им, конечно же, разрешено составлять для них список (фактически, уровень совместимости Emacs Lisp для CL делает именно это - но это, решительно и намеренно, не Обычная реализация Lisp).
Цель
Однако цель этого средства состоит в том, чтобы разрешить передачу (по крайней мере, некоторых) нескольких значений без обработки (т. Е. Без выделения динамической памяти), и все известные мне реализации CL делают это. В этом смысле средство множественных значений является оптимизацией.
Конечно, реализация этой функции может сильно отличаться для разных платформ и сценариев. Например, первые несколько (скажем, 20 - требуется по стандарту) хранятся в статическом векторе локального потока, следующие несколько (1000?) Распределяются в стеке, а остальные (при необходимости) распределяются в куча как вектор или список.
использование
Например, функция floor
возвращает два значения. Если ты пишешь
(setq a (floor 10 3))
вы захватываете только первый и отбрасываете второй, вам нужно написать
(setf (values q r) (floor 10 3))
захватить оба значения. Это похоже на то, что другие языки могут выражать как
q,r = floor(10,3)
используя кортежи, за исключением того, что CL не выделяет память для передачи (всего лишь нескольких) нескольких значений, как часто делают другие языки.
Таким образом, можно представить несколько значений как эфемерную структуру.
Обратите внимание, что CL может конвертировать несколько значений в списки:
(destructuring-bind (q r) (multiple-value-list (floor 10 3))
; use q & r here
...)
вместо более эффективного и лаконичного
(multiple-value-bind (q r) (floor 10 3)
; use q & r here
...)
MV & тип
CL не имеет специального типа для "объекта с несколькими значениями" именно потому, что он не выделяет отдельный объект для передачи нескольких значений. В этом смысле можно действительно утверждать, что values
является синтаксическим сахаром.
Однако в CL можно объявить тип функции, возвращающий несколько значений:
(declaim (ftype (real &optional real) (values real real)) floor)
Это означает, что floor
возвращает два значения, оба real
s (в отличие от возврата значения типа (values real real)
), т. е. в этом случае можно требовать злоупотребления обозначениями.
Ваш случай
В вашем конкретном случае, type-of
это обычная функция (т. е. не макрос или специальный оператор). Вы передаете ему один объект, 1, потому что, если вы не используете multiple-value-bind
и друзья, используется только первое значение, поэтому
(type-of (values 1 2 3))
идентично
(type-of 1)
и тип 1 является bit
,
PS: контроль возвращаемых значений
Одно использование values
это контролировать возвращаемые значения функции. Обычно возвращаемые значения функции CL соответствуют последней форме. Иногда это нежелательно, например, последняя форма возвращает несколько значений, и вы хотите, чтобы ваша функция возвращала одно значение (или ни одного, как void
в C
):
(defun 2values (x y)
(floor y x))
(defun 1value (x y)
(values (floor y x)))
(defun no-values (x)
(print x)
(values))
values
функция не просто синтаксический сахар для составления списка для вызывающего абонента для деструктуры.
Например, если вызывающая сторона ожидает только одно значение, она получит только одно значение (первое), а не список, из формы, которая возвращает несколько значений. поскольку type-of
принимает в качестве аргумента только одно значение, оно дает вам тип первого значения, 1. 1 имеет тип BIT.
Каждая реализация Common Lisp может использовать свою собственную стратегию реализации нескольких значений. Я многому научился из того, что написал Фроде Фьельд о том, как его реализация, Movitz, обрабатывает это в платформе разработки Movitz, раздел 2.5.
Если вы делаете реализацию CL, вы можете реализовать ее со списками, если она соответствует спецификации. Вам нужно обрабатывать одно конкретное значение, и вам нужно каким-то образом пометить ноль, значения 2..n, а другие функции должны понимать, что формат и печать могут отображаться так же, как и в других форматах.
Более вероятный values
и его родственные функции - это оптимизация, при которой реализации используют стек вместо того, чтобы передавать значения в структуру списка, чтобы просто деструктурировать его на следующем уровне. В старые времена, когда оперативная память и процессор не были потрачены впустую, это было очень важно, но я сомневаюсь, что вы заметите реальные проблемы, если будете использовать destructuring-bind
вместо multiple-value-bind
сегодня.
Common Lisp сильно отличается от Scheme тем, что вы можете сделать функцию, например, положительной. floor
где в его вычислениях получается остаток от дополнительного ответа, возвращайте все значения одновременно, но вы можете использовать его так, как если бы он возвращал только первое значение. Я действительно скучаю по этому иногда, когда пишу Схему, так как она требует от вас call-with-values
это похоже на multiple-value-call
или синтаксический сахар, как let-values
обрабатывать все возвращаемые значения, что опять-таки заставляет вас делать три версии на тот случай, если вам нужно только одно из значений.