2010-03-07 8 views
5

Acabo de empezar a jugar con Clojure, y escribí un pequeño script para ayudarme a entender algunas de las funciones. Comienza así:Confundido por "dejar" en Clojure

(def *exprs-to-test* [ 
    "(filter #(< % 3) '(1 2 3 4 3 2 1))" 
    "(remove #(< % 3) '(1 2 3 4 3 2 1))" 
    "(distinct '(1 2 3 4 3 2 1))" 
]) 

Entonces va a través *exprs-to-test*, todos ellos evalúa, e imprime la salida como esta:

(doseq [exstr *exprs-to-test*] 
    (do 
     (println "===" (first (read-string exstr)) "=========================") 
     (println "Code: " exstr) 
     (println "Eval: " (eval (read-string exstr))) 
    ) 
) 

El código anterior es todo funciona bien. Sin embargo, (read-string exstr) se repite así que traté de usar let para eliminar la repetición de este modo:

(doseq [exstr *exprs-to-test*] 
    (let [ex (read-string exstr)] (
     (do 
      (println "===" (first ex) "=========================") 
      (println "Code: " exstr) 
      (println "Eval: " (eval ex)) 
     ) 
    )) 
) 

Pero esto funciona una vez por el primer elemento de *exprs-to-test*, a continuación, se bloquea con un NullPointerException. ¿Por qué la adición de let causa el bloqueo?

Respuesta

7

Tiene un conjunto adicional de paréntesis alrededor del formulario do. Su código está haciendo esto:

((do ...)) 

Está tratando de ejecutar (como una llamada de función) el valor de toda la forma do, pero do está volviendo nil, porque la última println en forma do devuelve nil.

Tenga en cuenta que su estilo de sangría no es estándar. No debe poner los parens de cierre en sus propias líneas. Y let tiene un do implícito, por lo que no necesita uno allí. Prueba esto:

user> (doseq [exstr *exprs-to-test*] 
     (let [ex (read-string exstr)] 
      (println "===" (first ex) "=========================") 
      (println "Code: " exstr) 
      (println "Eval: " (eval ex)))) 
=== filter ========================= 
Code: (filter #(< % 3) '(1 2 3 4 3 2 1)) 
Eval: (1 2 2 1) 
=== remove ========================= 
Code: (remove #(< % 3) '(1 2 3 4 3 2 1)) 
Eval: (3 4 3) 
=== distinct ========================= 
Code: (distinct '(1 2 3 4 3 2 1)) 
Eval: (1 2 3 4) 
+0

Eso lo solucionó. Gracias por el consejo de estilo de sangrado también. –

1

Brian ya ha respondido a su pregunta, por lo que sólo quiero darle algunas sugerencias generales para la let-formulario:

4

Creo que las otras respuestas están ignorando al elefante en la habitación: ¿por qué estás haciendo esto? Hay muchas cosas en su código que hacen que me preocupe que está forjando en el camino equivocado a través de aprender Clojure:

  • Utilizando fijaciones globales (exprs-a-prueba)
  • Usando doseq/println a probar el código en la secuencia
  • Usando eval

la mejor manera de aprender las API de Clojure es a través de la réplica. Debería configurar su entorno, ya sea Vim, Emacs o un IDE, de modo que pueda avanzar y retroceder fácilmente entre el código estático en archivos de texto y un REPL interactivo. Here is a good breakdown of a number of Clojure IDEs.

Ahora, en lo que respecta a su código, algunas cosas para recordar. Primero, casi nunca hay una buena razón para usar eval. Si te encuentras haciendo esto, pregúntate si es realmente necesario. Segundo, recuerde, Clojure es un lenguaje funcional y generalmente no debería necesitar usar el conjunto de macros "do".Las macros "do" son útiles cuando se necesitan efectos secundarios (en el ejemplo, el efecto secundario es la impresión *) Finalmente, también se deben evitar los vars globales. Si necesita usar vars, debería considerar usar la macro bindings para vincular los vars localmente a la cadena a valores inmutables para que no haya problemas de simultaneidad.

Definitivamente recomiendo que se tome el tiempo para elegir Programación Clojure u otra referencia más profunda a LISP para comprender realmente el cambio necesario en la forma en que piensa acerca de la programación para aprovechar Clojure de manera efectiva. Tu pequeña muestra aquí me hace sentir como si estuvieras tratando de escribir código imperfecto en Clojure, lo cual no va a funcionar para nada.

+0

Sé que los globales están fuera de control en este script de 10 líneas, y ya no usaré las macros "do" y eval, incluso en situaciones donde realmente son necesarias. También tomaré su consejo sobre el REPL porque tengo un recuerdo impecable de los idiomas que solo he usado durante 10 minutos, y no necesito guardar mi trabajo para referencia futura. Como un experimentado profesional de Clojure, tu falta de respuesta correcta me llena de vergüenza. Trataré más la próxima vez. –

+0

¡Qué comentario sarcástico y sarcásticamente asqueroso! Estaba tratando de ayudar. Qué verguenza. –

+0

No creo que sea apropiado ignorar la pregunta y explicar cómo el código de un principiante no es perfecto. Me sentí más como si estuvieras soplando tu propia trompeta que tratando de ayudar. –