Как уменьшить список булевых значений в Common Lisp?

Учитывая список значений, я хочу уменьшить список до T, если все элементы не являются NIL, NIL, если нет. Это дает мне ошибку:

(apply #'and (get-some-list))

Как это делает:

(reduce #'and (get-some-list))

Это лучшее, что я придумал:

[11]> (defun my-and (x y) (and x y))
MY-AND

[12]> (reduce #'my-and '(T T T T T))
T

[13]> (reduce #'my-and '(T T T T NIL))
NIL

Почему "#" и "недействительны? Есть ли более идиоматический способ сделать это в Common Lisp?

Ответ 1

#'and является недопустимым, поскольку and является макросом, а не функцией.

Вы можете обойтись, чтобы определить именованную функцию, используя lambda:

(reduce (lambda (x y) (and x y)) (get-some-list) :initial-value t)

Нет ярлыка, например #'.

В качестве альтернативы вы также можете использовать every с функцией идентификации как предикат.

Ответ 2

Вы можете использовать функцию EVERY:

(every #'identity '(T T T T T))  ->  T

и

(every #'identity '(T T T T NIL))  ->  NIL

Вероятно, наиболее эффективным способом является использование LOOP:

(loop for element in '(T T T T nil) always element)  ->  NIL

Преимущество заключается в том, что никакие вызовы функций над элементами списка не требуются.

#' - это макрос чтения, который расширяется в FUNCTION во время чтения выражения. Итак #'and есть (FUNCTION AND).

ФУНКЦИЯ описывается здесь: http://www.lispworks.com/documentation/HyperSpec/Body/s_fn.htm

FUNCTION принимает имя функции или выражение лямбда и возвращает соответствующий объект функции.

AND определяется здесь: http://www.lispworks.com/documentation/HyperSpec/Body/m_and.htm

В нем говорится, что AND является макросом, а не функцией. Следствием этого является то, что (FUNCTION AND) не работает, поскольку для функции FUNCTION требуется функция, а не макрос, чтобы возвращать соответствующий объект функции. Как описывает sepp2k в своем ответе, вы можете создать функцию с использованием LAMBDA и использовать макрос AND внутри этой функции. Макросы не могут передаваться как значения, а затем вызываться через FUNCALL или APPLY. Это работает только с функциями.

Это решение написано как

(reduce (lambda (x y) (and x y)) (get-some-list))

LAMBDA - это макрос, который расширяет (lambda (...) ...) на (function (lambda (...) ...)).

Итак, это действительно так:

(reduce (function (lambda (x y) (and x y))) (get-some-list))

который можно записать как

(reduce #'(lambda (x y) (and x y)) (get-some-list))

ФУНКЦИЯ необходима, потому что Common Lisp делает разницу между пространством имен для значений и функций. REDUCE должен получить функцию, переданную как аргумент по значению. Поэтому нам нужно получить функцию из пространства имен функций, что является целью FUNCTION. Всякий раз, когда мы хотим передать объект функции, нам нужно получить его из пространства имен функций.

Например, в случае локальной функции:

(flet ((my-and (x y) (and x y)))
  #'my-and)

LAMBDA в качестве удобного макроса, который расширяется (FUNCTION (LAMBDA...)) был добавлен при разработке Common Lisp.

Ответ 3

Вы можете использовать символ "острая кавычка" только с обычными функциями.

Note that only ordinary functions can be quoted with #’. It is an error to
quote a macro function or special function this way, or to quote a symbol with
#’ if that symbol does not name a function.

> #’if
Error: IF is not an ordinary function.

COMMON LISP: Нежное введение к Symbolic Computation, страница 202