Exception 如何通过调用reduce来构建向量

Exception 如何通过调用reduce来构建向量,exception,vector,clojure,arity,Exception,Vector,Clojure,Arity,我试图弄明白为什么这个特殊的函数不能按预期工作。我从错误消息中怀疑它与我为累加器创建空向量的方式有关 我有一个简单的函数,它返回一个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]

我试图弄明白为什么这个特殊的函数不能按预期工作。我从错误消息中怀疑它与我为累加器创建空向量的方式有关

我有一个简单的函数,它返回一个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的方式来做我想做的事情,我也很想听听。

问题可能在于
accum的else子句,如果满足
,应该是
acc
而不是
(acc)

您可以使用
filter
,然后使用
map
而不是
reduce
。就像这样:

(map #(first %) 
     (filter #(p (second %))
             (zip-with-index coll)))
您还可以使用
向量调用
映射索引
,而不是
(fn[ic][ic])
。 整个代码如下所示:

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

问题可能出在
acum的else子句上,如果满足
,应该是
acc
而不是
(acc)

您可以使用
filter
,然后使用
map
而不是
reduce
。就像这样:

(map #(first %) 
     (filter #(p (second %))
             (zip-with-index coll)))
您还可以使用
向量调用
映射索引
,而不是
(fn[ic][ic])
。 整个代码如下所示:

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

至于更像Clojure的方式,你可以使用

(defn indexes-satisfying [pred coll]
  (filterv #(pred (nth coll %))
           (range (count coll))))
使用
filter
而不是
filterv
返回延迟序列而不是向量

此外,不应使用
defn
定义内部函数;相反,它将在定义内部函数的名称空间中定义一个全局函数,除此之外还有微妙的副作用。改用
letfn

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

至于更像Clojure的方式,你可以使用

(defn indexes-satisfying [pred coll]
  (filterv #(pred (nth coll %))
           (range (count coll))))
使用
filter
而不是
filterv
返回延迟序列而不是向量

此外,不应使用
defn
定义内部函数;相反,它将在定义内部函数的名称空间中定义一个全局函数,除此之外还有微妙的副作用。改用
letfn

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

另一种方法是:

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

另一种方法是:

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

这正是问题所在。这么小的事情。我也更喜欢你的版本。这正是问题所在。这么小的事情。我也更喜欢你的版本。这个实现比我想要的要干净得多。我不知道嵌套的defn泄漏到了全局名称空间中。这是个好消息。我假设作用域规则类似于Scala。
def*
表单并不是用来引入本地绑定的
let
letfn
可用于此功能。
def
的唯一目的是在当前名称空间中创建全局变量(其中“当前名称空间”在编译时确定)
defn
defmacro
等扩展到
def
。此外,那些
def
引入的全局变量会立即创建,即使
def
表单埋在函数体中,比如说,在“执行”该片段代码之前,它们不会收到任何绑定。(当然,有人可以在这两个时间点之间给Var一个根绑定。)这个实现比我想要的要干净得多。我不知道嵌套的defn泄漏到了全局名称空间中。这是个好消息。我假设作用域规则类似于Scala。
def*
表单并不是用来引入本地绑定的
let
letfn
可用于此功能。
def
的唯一目的是在当前名称空间中创建全局变量(其中“当前名称空间”在编译时确定)
defn
defmacro
等扩展到
def
。此外,那些
def
引入的全局变量会立即创建,即使
def
表单埋在函数体中,比如说,在“执行”该片段代码之前,它们不会收到任何绑定。(当然,有人可能会在这两个时间点之间出现,并给Var一个根绑定。)