2010-12-26 6 views
11

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

(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?

ответ

8

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

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

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

Там нет ярлыка, как #' хотя.

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

+1

Ваш пример ** неверен **, потому что первый аргумент 'reduce' может быть вызван либо с помощью нуля, либо с двумя аргументами" (см. CL hyperspec). Кроме того, вы не предоставляете никаких «начальных значений». В целом, ваш пример не работает, если вы хотите уменьшить пустой список или списки длиной 1. Правильная версия, например, '(lambda (& optional (x T) (y T)) (и xy))' , –

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: A Gentle Introduction to Symbolic Computation, page 202

19

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

(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 (ФУНКЦИЯ И).

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

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

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

Он говорит, что и макрос, а не функция. Следствием этого является то, что (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.

 Смежные вопросы

  • Нет связанных вопросов^_^