2012-04-22 2 views
0

Я работаю над алгоритмом Clojure для решения проблемы, поставленной здесь: http://spin.atomicobject.com/2011/05/31/use-clojure-to-move-drugs-a-programming-challenge/, и я столкнулся с икотой.Clojure возвращает значения из рекурсивного вызова

Я использую рекурсивный алгоритм (возможно, не правильный выбор для начала), чтобы пройти по вектору «кукольных» структур, упорядоченных по соотношению от максимальной к самой низкой величине к весу. Соответствующий код:

(defn get-carryable-dolls 
    [dolls carryable-dolls] 
    (def doll (first dolls)) ;initializing for use in multiple places 
    (def rest-dolls (rest dolls)) ;initializing for use in multiple places 
    (
    if (will-fit? doll (get-weight-sum carryable-dolls)) 
    (;will fit 
     (
     if 
     (= carryable-dolls {}) 
     (def new-doll-set [doll]) ;First trip, get rid of empty set by initializing new 
     (def new-doll-set (flatten [doll carryable-dolls])) ;otherwise, flatten set into vector of structs 
    ) 
     ;tests to see if we have any more dolls to test, and if so, recurses. Otherwise, should pass the resultant vector 
     ;up the stack. it appears to be the "else" potion of this if statement that is giving me problems. 
     (if (not=() rest-dolls) (get-carryable-dolls rest-dolls new-doll-set) (vec new-dolls)) 
    ) 
    (;will not fit 
     ;gets the rest of the dolls, and sends them on without modifying the vector of structs 
     ;it appears to be the "else" potion of this if statement that is giving me problems. 
     (if (not=() rest-dolls) (get-carryable-dolls rest-dolls carryable-dolls) (vec carryable-dolls)) 
    ) 
) 
) 

Этот код работает правильно; Возвращаемые куклы содержат желаемый вектор кукольных структур, возвращаемых в качестве решения. К сожалению, когда я пытаюсь вернуть вектор возвратной-куклы в позицию вызывающей, я получаю следующее сообщение об ошибке:

CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: PersistentVector,compiling:(drugmover\tests.clj:83) 

линия 82-83 чтения:

(def empty-dolls {}) 
(def designated-dolls (get-carryable-dolls sorted-values empty-dolls)) 

Я озадачен, как что может вызвать проблему в ошибке компилятора, и поскольку Clojure, похоже, предпочитает короткие сообщения об ошибках по трассировке стека (или, по крайней мере, функциональность REPL в Clooj), я не понимаю, как это исправить. Если у кого-нибудь есть предложения, я бы очень признателен им!

Заранее спасибо.

EDIT:

Я изменил код с предложенными исправлениями теми, в ответах и ​​комментариях, и представила несколько замечаний, чтобы проиллюстрировать управление потоком, что происходит. Надеюсь, что, иллюстрируя мое мышление, кто-то сможет дать мне представление о том, где я ошибаюсь.

+1

Вы дождевания скобку во всем коде, как если бы они были фигурные скобки. –

+0

Можете ли вы указать конкретный пример того, что я мог бы удалить круглые скобки и все равно получить тот же результат? Я очень, очень новичок в этом. – EricBoersma

+0

Например: сразу после условия первого 'if' имеются две открывающиеся скобки; первый не нужен, второй правильный, потому что это открывающая скобка второго 'if'. –

ответ

4

следующая с ода включает в себя большинство предложений вы уже получили в других ответах и ​​других, а именно:

  • избавившись от superflous (и неправильно) скобки
  • форматирования коды в более идиоматическом способе
  • использование loop/let вместо def для локальных имен привязки
  • использование seq для проверки пустых списков
  • удалить ненужную проверку на пустой carryable-dolls SEQ перед добавлением элемент

Я не могу проверить его, не имея определения вспомогательных функций, которые у вас есть (например, will-fit?), но, по крайней мере, должна решить несколько проблем (и привести код в более читаемом виде):

(defn get-carryable-dolls 
    [dolls carryable-dolls] 
    (loop [doll (first dolls) 
     rest-dolls (rest dolls)] 
    (if (will-fit? doll (get-weight-sum carryable-dolls)) 
     (let [new-doll-set (if (seq carryable-dolls) 
          (cons doll carryable-dolls) 
          [doll])] 
     (if (seq rest-dolls) 
      (recur rest-dolls new-doll-set) 
      (vec new-dolls))) 
     (if (seq rest-dolls) 
     (recur rest-dolls carryable-dolls) 
     (vec carryable-dolls))))) 

Ниже приводится полный рефакторинг кода, который использует стандартную reduce функции и определяет функцию что обеспечивает логику решения ядра ли должна быть включена кукла или нет в результате:

(defn add-if-fits [dolls doll] 
    (if (will-fit? doll (get-weighted-sum dolls)) 
    (cons doll carryable-dolls) 
    carryable-dolls)) 

(defn get-carryable-dolls [dolls carryable-dolls] 
    (reduce add-if-fits carryable-dolls dolls)) 
+1

skuro, спасибо за помощь. Ваше второе решение обеспечивает решение, которое я искал, и, очевидно, гораздо более кратким, чем тот, который я изначально разместил. Ты спасатель жизни! – EricBoersma

3

В этом коде есть путь, слишком много парнеров, и они вызывают проблемы. Я настоятельно рекомендую вам форматировать свой код так, как это делают все остальные, что сделает ошибки, подобные этому, легко выделяться. Я не могу даже предположить, что вы пытаетесь сделать, так что я не могу переписать весь фрагмент кода, но соответствующая вещь, чтобы отметить, что синтаксис if является:

(if test then else) 

Никаких дополнительных круглых скобок не допускается вокруг любой из этих вещей: (if true 1 2) в порядке, например, но (if (true) 1 2) попытался бы вызвать true как функцию и потерпеть неудачу, потому что это логическое значение. Если вы хотите «группировать» выражения вместе и оценивать их для побочных эффектов, вы хотите (do expr1 expr2), а не (expr1 expr2).

+0

Спасибо за предложения. Я рассмотрю, как уменьшить круглые скобки и посмотреть, могут ли они давать какие-либо подсказки. Такова опасность попыток решить проблему кода, подобную этой, в течение 24 часов после использования языка в первый раз. – EricBoersma

+0

Итак, если он уже вычисляет правильное значение, но затем не работает, потому что он случайно вызывает что-то как функцию, вы можете исправить его, заменив каждый экземпляр '((a) (b))' на '(do (a) (б)) '. После того, как вы познакомитесь с clojure, вы можете вернуться назад и удалить все 'def' изнутри этой функции, которые являются передержкой из очень императивного стиля. – amalloy

+0

Я не уверен, что он терпит неудачу, потому что он случайно вызывает что-то как функцию. И это, скорее всего, моя неопытность в разговоре с языком, но я честно не вижу ни одного примера круглых скобок в вставленных операциях, которые можно удалить. Не могли бы вы привести более конкретный пример? – EricBoersma

3

У вас есть дополнительные скобки вокруг вашего if кода и что является причиной этой ошибки:

Код ниже будет производить ту же ошибку, как это имеет дополнительные скобки (тот же случай в вашей, если когда-то часть называется создать вектор:

((vec {:a 10 :b 100})) 

Попробуйте выполнить это в REPL, и вы увидите то же самое исключение:

java.lang.IllegalArgumentException: Wrong number of args (0) passed to: PersistentVector (NO_SOURCE_FILE:0) 
Смежные вопросы