2010-12-26 21 views
11

Dada una lista de valores, quiero reducir la lista a T si todos los elementos no son NIL, NIL si no. Esto me da un error:¿Cómo se puede reducir una lista de valores booleanos en Common Lisp?

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

Como se hace esto:

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

Este es el mejor que he llegado con:

[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 

¿Por qué es "#" y" no válido ? ¿Hay una forma más idiomática de hacer esto en Common Lisp?

Respuesta

8

#'and no es válido porque and es un macro, no es una función.

Usted puede conseguir alrededor de tener que definir una función llamada, mediante el uso de un lambda:

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

No hay acceso directo al igual que #' sin embargo.

Alternativamente, también puede usar every con la función de identificación como el predicado.

+1

Su ejemplo es ** incorrecto ** porque la primera arg de 'reduce'" podría llamarse con cero o dos argumentos "(vea CL hyperspec). Además, no proporciona ningún 'valor inicial'. En general, su ejemplo no funciona cuando desea reducir la lista o listas vacías con longitud 1. Una versión correcta es, por ejemplo, '(lambda (y opcional (x T) (y T)) (y xy))' . –

3

Puede usar el símbolo 'comilla aguda' solo con funciones normales.

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

Puede utilizar la función TODOS:

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

y

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

Probablemente la forma más eficiente es el uso de LOOP:

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

La ventaja es que no se necesitan llamadas a funciones sobre los elementos de la lista.

#' es una macro de lectura que se expande en FUNCIÓN durante la lectura de la expresión. Entonces #'and es (FUNCIÓN Y).

función se describe aquí: http://www.lispworks.com/documentation/HyperSpec/Body/s_fn.htm

función toma un nombre de función o una expresión lambda y devuelve el objeto de función correspondiente.

y se define aquí: http://www.lispworks.com/documentation/HyperSpec/Body/m_and.htm

Se dice que Y es una macro, no una función. La consecuencia es que (FUNCTION AND) no funciona, ya que FUNCTION necesita una función y no una macro para devolver el objeto de función correspondiente. Como sepp2k describe en su respuesta, puede crear una función usando LAMBDA y usar la macro Y dentro de esa función. Las macros no se pueden pasar como valores y luego se pueden llamar a través de FUNCALL o APLICAR. Esto funciona solo con funciones

Esta solución se escribe como

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

LAMBDA es una macro que se expande (lambda (...) ...) en (function (lambda (...) ...)).

Así anterior es verdad:

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

que se puede escribir como se necesita

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

función porque Common Lisp hace una diferencia entre el espacio de nombres para los valores y funciones. REDUCE necesita pasar la función como argumento por valor. Entonces, necesitamos recuperar la función del espacio de nombres de la función, que es el propósito de FUNCTION. Siempre que deseemos pasar un objeto de función, debemos obtenerlo del espacio de nombres de la función.

Por ejemplo, en el caso de una función local:

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

LAMBDA como una macro conveniencia que se expande en (function (LAMBDA ...)) se ha añadido durante el diseño de Common Lisp.