2009-11-10 14 views
5

Niedawno zacząłem czytać "On Lisp" Paula Grahamsa i ucząc się nauki clojure wraz z nim, więc prawdopodobnie jest tu jakiś oczywisty błąd, ale nie widzę tego: (jest to oczywiście problem z eulerem projektu)Co jest nie tak z tym programem 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) 

Kiedy biegnę ostatni wiersz w REPL, pojawia się błąd:

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

Oprócz pytanie gdzie jest błąd, tym bardziej interesujące byłoby pytanie: w jaki sposób jeden debugowania to? Komunikat o błędzie nie jest bardzo pomocny i nie znalazłem dobrego sposobu na jednokierunkowy kod typu clojure i nie mogę naprawdę zapytać o przepełnienie stosu za każdym razem, gdy mam problem.

+0

Doskonałe pytanie. Komunikaty o błędach mogą być dość wrogie! –

Odpowiedz

13

Trzy wskazówki, które mogłyby uczynić łatwiej tu swoje życie:

  1. Wrong number of args passed to: problem31$eval--25$make-combination-counter--27$recurse--29$fn (NO_SOURCE_FILE:0)> Informuje, z grubsza, w którym wystąpił błąd: $fn w końcu nie oznacza funkcja anonimowa i mówi, że został uznany wewnątrz recurse, który został zadeklarowany wewnątrz make-combination-counter. Do wyboru są dwie anonimowe funkcje.

  2. Jeśli zapiszesz kod źródłowy w pliku i wykonasz go jako skrypt, otrzymasz pełny ślad stosu z numerami linii w pliku.

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

    Uwaga Można także zbadać ostatnią wyjątku i ślad stosu od wewnątrz REPL badając * e np: (.stackTrace * e) Ślad stosu jest początkowo dość trudne, ponieważ rzuca się wszystkie wewnętrzne Java . Musisz nauczyć się ignorować te i po prostu szukać linii, które odnoszą się do twojego kodu. Jest to dość proste w przypadku gdy wszyscy zaczną z net.projecteuler

  3. Możesz wymienić swoje anonimowe funkcje, które pomogą szybciej zidentyfikować je:

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

W twoim przypadku wykorzystując wszystkie te informacje co można zobacz, że apply-if jest przekazywana jako funkcja pojedynczego argumentu jako zabawa. Zastosuj to (f [1 2 3]) -> (f 1 2 3). Z Twojego komentarza chcesz uzyskać mapę. (mapa f [1 2 3]) -> (lista (f 1) (f 2) (f 3)). Po zamianie apply na map program wydaje się działać.

Na koniec, jeśli chcesz sprawdzić wartości, możesz zajrzeć do clojure-contrib.logging, która ma pomocników w tym zakresie. Istnieje makro szpiegowskie, które umożliwia zawijanie wyrażenia, zwraca dokładnie to samo wyrażenie, aby nie wpłynęło to na wynik funkcji, ale wydrukuje EXPR = VALUE, co może być przydatne. Również w grupie różne osoby opublikowały pełne rozwiązania śledzące. I zawsze jest zaufany println. Ale kluczową umiejętnością jest umiejętność dokładnego określenia, co wybuchło. Kiedy już wiesz, że jest to zwykle jasne, dlaczego, ale czasami wydruki są potrzebne, gdy nie możesz powiedzieć, jakie są dane wejściowe.

2

nie masz rEPL na mnie chociaż to wygląda:

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

pobiera listę jak '(1 2 3 4 5) filtruje niektóre z nich się '(1 3 5) a następnie tworzy wywołanie funkcji jak (fun 1 3 5)

i wygląda na to, nazywa się go (apply-if (fn [x] z funkcją, która chce otrzymać listę numerów jako pojedynczy argument.

można zmienić funkcję apply-if, aby po prostu przekazać połączenie do zabawy (bez użycia) lub można zmienić wywołanie, aby przyjęła funkcję, która pobiera dowolną liczbę argumentów.