Подтвердить что ты не робот

Значения в Common Lisp

Является ли функция значений в Common Lisp просто синтаксическим сахаром для упаковки нескольких значений в список, который разрушается вызывающим? Я спрашиваю, потому что я думал, что Common Lisp поддерживает "истинный" множественный возврат значения, а не возвращает кортеж или список, как на других языках, таких как python. Кто-то просто сказал мне, что это просто синтаксический сахар, поэтому я хотел бы, чтобы кто-то любезно объяснил это. Чтобы попытаться понять тип, возвращаемый функцией значений, я набрал (type-of (values 1 2 3)), а результат был BIT. Я искал в Common Lisp ссылку для этого, и я не мог найти его упомянутым в разделе datatypes. Кроме того, может ли кто-нибудь поделиться некоторыми ресурсами, которые предлагают, как функция значений реализуется в Common Lisp?. Спасибо.

4b9b3361

Ответ 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, чтобы обрабатывать все возвращаемые значения, которые снова заставляют вас создавать три версии, если вы требуется только одно из значений.