2013-05-09 2 views
1

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

У меня есть простая функция, которая возвращает последовательность векторов 2-элементных:

(defn zip-with-index 
    "Returns a sequence in which each element is of the 
    form [i c] where i is the index of the element and c 
    is the element at that index." 
    [coll] 
    (map-indexed (fn [i c] [i c]) coll)) 

Это работает отлично. Проблема возникает, когда я пытаюсь использовать его в другой функции

(defn indexes-satisfying 
    "Returns a vector containing all indexes of coll that satisfy 
    the predicate p." 
    [p coll] 
    (defn accum-if-satisfies [acc zipped] 
    (let [idx (first zipped) 
      elem (second zipped)] 
     (if (p elem) 
     (conj acc idx) 
     (acc)))) 
    (reduce accum-if-satisfies (vector) (zip-with-index coll))) 

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

user=> (indexes-satisfying (partial > 3) [1 3 5 7]) 
ArityException Wrong number of args (0) passed to: PersistentVector 
clojure.lang.AFn.throwArity (AFn.java:437) 

Я не могу понять, что происходит не так Вот. Кроме того, если есть более «способ, похожий на Clojure» на то, что я пытаюсь сделать, мне также интересно узнать об этом.

ответ

2

Проблема, вероятно, в разделе else accum-if-satisfies, должна быть только acc не (acc).

Вы можете использовать filter, а затем map вместо reduce. Как что:

(map #(first %) 
    (filter #(p (second %)) 
      (zip-with-index coll))) 

Вы также могли бы назвать map-indexed с vector вместо (fn [i c] [i c]). Весь код будет выглядеть так:

(defn indexes-satisfying 
    [p coll] 
    (map #(first %) 
     (filter #(p (second %)) 
       (map-indexed vector coll)))) 
+0

Это было именно проблема. Такая маленькая вещь. Мне нравится ваша версия намного лучше. –

2

Что касается более Clojure-подобным образом, вы могли бы использовать

(defn indexes-satisfying [pred coll] 
    (filterv #(pred (nth coll %)) 
      (range (count coll)))) 

Использование filter вместо filterv вернуть ленивый SEQ вместо вектора.

Кроме того, вы не должны использовать defn для определения внутренних функций; он вместо этого определит глобальную функцию в пространстве имен, где определена внутренняя функция и кроме того есть более тонкие побочные эффекты. Используйте letfn вместо:

(defn outer [& args] 
    (letfn [(inner [& inner-args] ...)] 
    (inner ...))) 
+0

Эта реализация намного чище, чем то, что я собираюсь сделать. Я не знал, что вложенные defn просочились в глобальное пространство имен. Это хорошая информация. Я принимал правила съемки, подобные Scala. –

+2

'def *' формы просто не предназначены для введения локальных привязок; 'let' и' letfn' доступны для этого. Единственной целью 'def' является создание глобальных Vars в текущем пространстве имен (где во время компиляции определено« текущее пространство имен »). 'defn',' defmacro' и такие расширяются до 'def'. Кроме того, эти 'def'-введенные глобальные Vars создаются сразу же, даже если форма' def' похоронена внутри тела функции, скажем, хотя они не получают привязки, пока этот кусочный код не будет «выполнен». (Конечно, кто-то мог прийти между двумя точками во времени и дать Var привязку к корню.) –

1

Еще один способ сделать это будет:

(defn indexes-satisfying [p coll] 
    (keep-indexed #(if (p %2) % nil) coll)) 

 Смежные вопросы

  • Нет связанных вопросов^_^