Что не так с этой программой Clojure? - PullRequest
5 голосов
/ 10 ноября 2009

Я недавно начал читать Пола Грэхамса «На Лиспе» и изучать изучение уловок вместе с ним, поэтому, возможно, здесь есть какая-то действительно очевидная ошибка, но я ее не вижу: (очевидно, это проблема проекта Эйлера) 1001 *

(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)

Когда я запускаю последнюю строку в REPL, я получаю сообщение об ошибке:

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

Помимо вопроса об ошибке, более интересным будет вопрос: как отладить это? Сообщение об ошибке не очень полезно, и я не нашел хорошего способа одношагового кода clojure, и я действительно не могу просить переполнение стека каждый раз, когда у меня возникает проблема.

Ответы [ 2 ]

13 голосов
/ 11 ноября 2009

Три совета, которые могут сделать вашу жизнь проще здесь:

  1. Wrong number of args passed to: problem31$eval--25$make-combination-counter--27$recurse--29$fn (NO_SOURCE_FILE:0)> Примерно сообщает вам, где произошла ошибка: $fn в конце означает анонимную функцию и сообщает, что она была объявлена ​​внутри recurse, который был объявлен внутри make-combination-counter. На выбор доступны две анонимные функции.

  2. Если вы сохраните свой исходный код в файле и выполните его как скрипт, он даст вам полную трассировку стека с номерами строк в файле.

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

    Обратите внимание, что вы также можете проверить последнее исключение и трассировку стека из REPL, изучив * e, например: (.stackTrace * e) Трассировка стека на первый взгляд довольно устрашает, поскольку она отбрасывает все внутренние компоненты Java. Вам нужно научиться игнорировать их и просто искать строки, которые относятся к вашему коду. Это довольно легко в вашем случае, так как все они начинаются с net.projecteuler

  3. Вы можете назвать свои анонимные функции, чтобы помочь быстрее идентифицировать их:

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

В вашем случае, используя всю эту информацию, вы можете видеть, что apply-if передается функция с одним аргументом в качестве забавы. Применить это (f [1 2 3]) -> (f 1 2 3). Из вашего комментария, что вы хотите, это карта. (карта f [1 2 3]) -> (список (f 1) (f 2) (f 3)). Когда я заменяю apply на map, программа, кажется, работает.

Наконец, если вы хотите изучить значения, вы можете посмотреть на clojure-contrib.logging, у которого есть некоторые помощники для этого эффекта. Есть шпионский макрос, который позволяет вам обернуть выражение, он будет возвращать точно такое же выражение, поэтому он не повлияет на результат вашей функции, но выведет EXPR = VALUE, что может быть удобно. Также в группе различные люди разместили полные решения для отслеживания. И всегда есть верный println. Но ключевым навыком здесь является способность точно определить, что взорвалось. Как только вы узнаете, что обычно понятно почему, но иногда требуются распечатки, когда вы не можете определить, какие вводные данные.

2 голосов
/ 10 ноября 2009

нет ответа на меня, хотя это выглядит так:

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

принимает список типа '(1 2 3 4 5) , отфильтровывает некоторые из них '(1 3 5) а затем создает вызов функции типа (fun 1 3 5)

и похоже, что он вызывается (apply-if (fn [x] с функцией, которая хочет получить список чисел в качестве единственного аргумента.

вы можете изменить функцию apply-if, чтобы просто передать вызов fun (без применения apply), или вы можете изменить вызов на нее, чтобы получить функцию, которая принимает произвольное количество аргументов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...