2009-11-10 15 views
5

Recientemente comencé a leer Paul Grahams 'On Lisp', y aprendí a aprender clojure junto con él, por lo que es probable que haya un error muy obvio aquí, pero no lo veo: (es un problema de proyecto euler, obviamente)¿Qué pasa con este programa de Clojure?

(ns net.projecteuler.problem31) 

(def paths (ref #{})) 

; apply fun to all elements of coll for which pred-fun returns true 
(defn apply-if [pred-fun fun coll] 
    (apply fun (filter pred-fun coll))) 

(defn make-combination-counter [coin-values] 
    (fn recurse 
    ([sum] (recurse sum 0 '())) 
    ([max-sum current-sum coin-path] 
     (if (= max-sum current-sum) 
      ; if we've recursed to the bottom, add current path to paths 
      (dosync (ref-set paths (conj @paths (sort coin-path)))) 
      ; else go on recursing 
      (apply-if (fn [x] (<= (+ current-sum x) max-sum)) 
       (fn [x] (recurse max-sum (+ x current-sum) (cons x coin-path))) 
       coin-values))))) 

(def count-currency-combinations (make-combination-counter '(1 2 5 10 20 50 100 200))) 
(count-currency-combinations 200) 

Cuando ejecuto la última línea en el REPL, me sale el error:

<#CompilerException java.lang.IllegalArgumentException: Wrong number of args passed to: problem31$eval--25$make-combination-counter--27$recurse--29$fn (NO_SOURCE_FILE:0)> 

Aparte de la cuestión de dónde está el error, la pregunta más interesante sería: ¿Cómo se podría depurar esto? El mensaje de error no es muy útil, y no he encontrado una buena forma de código clojure de un solo paso, y realmente no puedo preguntar sobre el desbordamiento de la pila cada vez que tengo un problema.

+0

Excelente pregunta. ¡Los mensajes de error pueden ser bastante hostiles! –

Respuesta

13

Tres consejos que pueden hacer su vida más fácil aquí:

  1. Wrong number of args passed to: problem31$eval--25$make-combination-counter--27$recurse--29$fn (NO_SOURCE_FILE:0)> Le dice más o menos donde se produjo el error: $fn al final no significa función anónima y se le dice que se declara dentro recursivo, el cual fue declarado dentro de make-combination-counter. Hay dos funciones anónimas para elegir.

  2. Si guarda su código fuente en un archivo y lo ejecuta como una secuencia de comandos, obtendrá un seguimiento completo de la pila con los números de línea en el archivo.

    at net.projecteuler.problem31$apply_if__9.invoke(problem31.clj:7) 
    

    Nota También puede examinar la última excepción y seguimiento de la pila dentro de la REPL examinando * e por ejemplo: (.stackTrace * e) El seguimiento de la pila es al principio muy desalentador ya que vomita todas las partes internas de Java . Debe aprender a ignorarlos y simplemente buscar las líneas que se refieren a su código. Esto es bastante fácil en su caso, ya que todos comienzan con net.projecteuler

  3. puede asignar nombres a funciones anónimas para ayudar a identificar más rápidamente ellos:

    (fn check-max [x] (<= (+ current-sum x) max-sum)) 
    

En su caso el uso de toda esta información que pueda ver que se aplica, si se pasa una función de argumento único como diversión. Aplicar hace esto (f [1 2 3]) -> (f 1 2 3). De tu comentario lo que quieres es mapa. (mapa f [1 2 3]) -> (lista (f 1) (f 2) (f 3)). Cuando reemplazo apply con map, el programa parece funcionar.

Finalmente, si desea examinar los valores, es posible que desee consultar clojure-contrib.logging, que tiene algunos ayudantes a este efecto. Hay una macro de espionaje que le permite ajustar una expresión, devolverá exactamente la misma expresión para que no afecte el resultado de su función, pero imprimirá EXPR = VALUE, que puede ser útil. También en el grupo varias personas han publicado soluciones completas de seguimiento. Y siempre está el confiable println. Pero la habilidad clave aquí es poder identificar con precisión qué explotó. Una vez que sepa que generalmente está claro por qué, a veces se necesitan impresiones cuando no puede decir cuáles son las entradas.

2

no tiene un REPL en mí a pesar de que parece:

(defn apply-if [pred-fun fun coll] 
    (apply fun (filter pred-fun coll))) 

toma una lista como '(1 2 3 4 5) filtra a algunos de ellos '(1 3 5) y luego crea una llamada de función como (fun 1 3 5)

y parece que se llama (apply-if (fn [x] con una función que quiere recibir una lista de números como un único argumento.

puede cambiar la función aplicar-si para pasar la llamada a la diversión (sin aplicar) o puede cambiar la llamada para tomar una función que toma un número arbitrario de argumentos.