2012-01-21 6 views
6

Estoy tratando de elegir Lisp como mi nuevo idioma, y ​​estoy teniendo algunos problemas para determinar cómo funcionan las partes de una función en cada elemento de la lista que se le pasa.defun con una lista como argumento

Con el fin de aprender cómo evitar esto, estoy tratando de escribir una forma bastante básico de la división no estirar la pata cuando uno de los elementos de la lista son 0 (pero en cambio sólo devuelve 0)

(defun divtest (elements) 
    (dolist (x elements) 
    (if (zerop x) 0()) 
    (/ elements))))) 

intento funcionar esto como:

(divtest '(20 2 5)) 

que produce:

*** - /: (20 2 5) is not a number 

El punto de falla parece estar arraigado en el hecho de que no estoy "extrayendo" los elementos en la lista antes de pasarlos a la función (en este caso, ni/ni dolist funciona como se esperaba, ya que x nunca se evalúa a 0). Si estoy en lo cierto, ¿alguien me puede decir cómo realizar esta "extracción"?


Nota: Esta pregunta está relacionada con one that I've asked earlier, pero que a mí no está claro sobre qué parte de la respuesta anterior permitió realmente que funcione como se pretende con este problema específico que decidí ir más lejos en los fundamentos

Respuesta

5

/ toma como argumentos uno o más números, pero en su código lo está pasando una lista - claramente esto no funcionará. La función apply es su amiga aquí - (apply #'foo a b (list c d e)) es equivalente a (foo a b c d e). Tenga en cuenta que los argumentos a apply entre la función a usar y la lista final son opcionales, por lo que (apply #'/ '(20 2 5)) es equivalente a (/ 20 2 5).

Además, su intento de eliminar ceros no funcionará. dolist está evaluando su cuerpo para cada elemento en la lista de argumentos elements, pero en realidad no está haciendo nada para cambiar el contenido de elements (el resultado de evaluar dolist s cuerpo no se reasigna al elemento de origen como parece esperar).

Las funciones remove-if (y su equivalente destructivo, delete-if) son lo que está buscando. A continuación, se muestra cómo usarlo (se necesitan muchos argumentos opcionales, de lo que no debe preocuparse).

(defun divtest (elements) 
    (apply #'/ (remove-if #'zerop elements))) 

También tenga en cuenta que esto no se comportará correctamente si la lista elements tiene cero como primer elemento (suponiendo que entiendo lo que quería hacer de la función). Por lo tanto, es posible que desee algo como

(defun divtest (elements) 
    (apply #'/ (first elements) (remove-if #'zerop (rest elements)))) 

Consulte el Hyperspec para obtener más detalles.

+2

use REDUCE en lugar de APLICAR –

+0

Simplemente curioso: ¿cuál sería la utilidad de eso? – Hugh

+0

de esta manera podría procesar listas largas arbitrarias y no solo las de CALL-ARGUMENT-LIMIT (una constante CL estándar) longitud máxima. Common Lisp tiene una implementación dependiente del número máximo de argumentos. Este número debe ser 50 o más grande. Esto significa que una implementación solo necesita admitir 50 argumentos (o más). Por lo tanto, su función anterior puede fallar en alguna implementación en la que la lista de números sea más larga que la cantidad de argumentos que llama a la función/permite. –

0

Pruebe (apply/elements) en lugar de (/ elements). Creo (?) Que debería funcionar en la mayoría de los dialectos de Lisp.

+0

Tendrá que citar que '/' a menos que haya asignado una función a 'celda de datos /' 's. – Hugh

1

O puede escribir como este

(defun divtest (elements) 
    (if (member 0 elements) 
     0 
     (apply #'/ elements))) 
1
(block exit 
    (reduce #'/ '(1 2 3 0 5) 
      :key (lambda (x) 
       (if (zerop x) 
        (return-from exit 0) 
        x)))) 
Cuestiones relacionadas