Является ли функция значений в Common Lisp просто синтаксическим сахаром для упаковки нескольких значений в список, который разрушается вызывающим? Я спрашиваю, потому что я думал, что Common Lisp поддерживает "истинный" множественный возврат значения, а не возвращает кортеж или список, как на других языках, таких как python. Кто-то просто сказал мне, что это просто синтаксический сахар, поэтому я хотел бы, чтобы кто-то любезно объяснил это. Чтобы попытаться понять тип, возвращаемый функцией значений, я набрал (type-of (values 1 2 3))
, а результат был BIT
. Я искал в Common Lisp ссылку для этого, и я не мог найти его упомянутым в разделе datatypes. Кроме того, может ли кто-нибудь поделиться некоторыми ресурсами, которые предлагают, как функция значений реализуется в Common Lisp?. Спасибо.
Значения в Common Lisp
Ответ 1
Несколько значений в CL
Язык Общий 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)
используя tuples, за исключением того, что CL не выделяет память для передачи (всего несколько) нескольких значений, а другие языки часто делают.
IOW, можно думать о нескольких значениях как эфемерной структуре.
Обратите внимание, что 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
(в отличие от возврата значения типа (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))
Ответ 2
Функция values
- это не просто синтаксический сахар для составления списка для деструкции вызывающего.
Во-первых, если вызывающий абонент ожидает только одно значение, он получит только одно значение (первое), а не список, из формы, которая возвращает несколько значений. Поскольку type-of
принимает только одно значение в качестве аргумента, он дает вам тип первого значения, 1. 1 имеет тип BIT.
Каждая реализация Common Lisp может свободно использовать собственную стратегию для реализации нескольких значений. Я многое узнал из того, что Фроде Фьелд написал о том, как его реализация, Movitz, обрабатывает его в платформе разработки Movitz, раздел 2.5.
Ответ 3
Если вы создаете реализацию CL, вы можете реализовать ее со списками, если она будет соответствовать спецификации. Вам нужно обработать одно значение, и вам нужно каким-то образом пометить ноль, значения 2..n, а другие функции должны понять, что формат и печать могут быть сделаны для отображения его так же, как и в других случаях.
Скорее всего, values
и его сестринские функции - это оптимизация, в которой реализации используют стек вместо того, чтобы ссылаться на значения в структуру списка только для того, чтобы разрушить его на следующем уровне. В прежние времена, когда RAM и CPU не были потрачены впустую, это было очень важно, но я сомневаюсь, что вы заметите настоящую проблему, если вы сегодня используете destructuring-bind
вместо multiple-value-bind
.
Общие Lisp сильно отличаются от Схемы в положительном направлении, которое вы можете сделать, например. floor
, где в результате вычисления заканчиваются вместе с остатком в дополнение к частному ответу, возвращают все значения одновременно, но вы можете использовать его, как если бы он возвращал только первое значение. Я действительно скучаю по тому, что иногда при написании схемы, поскольку она требует, чтобы у вас был call-with-values
, который похож на multiple-value-call
или синтаксический сахар, например let-values
, чтобы обрабатывать все возвращаемые значения, которые снова заставляют вас создавать три версии, если вы требуется только одно из значений.