【问题标题】:How to build a vector via a call to reduce如何通过调用 reduce 来构建向量
【发布时间】:2013-05-09 22:47:35
【问题描述】:

我试图弄清楚为什么这个特定的功能没有按预期工作。我从错误消息中怀疑它与我为累加器创建空向量的方式有关。

我有一个简单的函数,它返回一系列 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”的方式来做我想做的事情,我也有兴趣了解这一点。

【问题讨论】:

    标签: exception vector clojure arity


    【解决方案1】:

    问题可能出在accum-if-satisfies 的else 子句上,应该只是acc 而不是(acc)

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

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

    您也可以使用vector 而不是(fn [i c] [i c]) 调用map-indexed。 整个代码如下所示:

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

    【讨论】:

    • 这正是问题所在。这么小的东西。我也更喜欢你的版本。
    【解决方案2】:

    至于更类似于 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 泄漏到全局命名空间中。这是很好的信息。我假设范围规则类似于 Scala。
    • def* 表单并不意味着引入本地绑定; letletfn 可用于此。 def 的唯一目的是在当前命名空间中创建全局变量(其中“当前命名空间”在编译时确定)。 defndefmacro 等扩展为 def。此外,那些def-introduced 全局变量会立即创建,即使def 表单被埋在函数体中,例如,尽管在“执行”该片段代码之前它们不会收到任何绑定。 (当然有人可以在两个时间点之间出现并给 Var 一个根绑定。)
    【解决方案3】:

    另一种方法是:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-07
      • 2015-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-16
      • 2021-05-03
      相关资源
      最近更新 更多