在循环中定义一个map对象recur - Clojure

时间:2018-01-02 23:18:12

标签: json clojure functional-programming atom-editor

我正在尝试使用循环重复,在循环内创建一个空映射。对于循环中的每个条目(循环遍历映射向量),它将查看新创建的映射中是否存在与迭代值键匹配的键,如果没有则创建一个键。

我创建了这段代码:

(def meteor-map (json/read-str (clojure.string/lower-case
(slurp "https://data.nasa.gov/resource/y77d-th95.json"))))

(defn most-falls [values]
(loop [values map count-tracker{}]
  (if (empty? values)
    (count-tracker)
    (do
      (def key (keyword (get (first values) "year")))
      (if (contains? (first values) key)
        (do
          (def count-tracker (update count-tracker key inc))
          (recur (rest values) count-tracker)
        )
        (do
          (def count-tracker (assoc count-tracker key 1))
          (recur (rest values) count-tracker)
        )
      )
    )
  )
)
)

(most-falls meteor-map)

然而,当我调用此函数并传入meteor-map(这是一张地图矢量)时,我收到一条错误

  

错误的args(0)传递给persistentarraymap

我认为这可能是由于我如何在循环创建中创建初始count-tracker对象,但我不确定。

有什么想法吗?

由于

PS我知道这个问题有点含糊,所以任何问题都要问!

2 个答案:

答案 0 :(得分:3)

这里有很多东西要提出来。你的主要问题是(count-tracker)。您在括号内围绕地图,这意味着您要将其称为函数。您不能像在其他语言中那样随意添加括号到代码;它在Clojure中具有非常特定含义。 (f) 始终表示正在调用函数f。只需将其更改为count-tracker即可返回值。

其他事项:

  • 除非必要,否则在函数内部使用def。但在这种情况下,完全没必要。 def的每次使用都会创建持续程序长度的全局变量(是的,即使在函数退出后它们也存在!)。请改用let

    (let [key (keyword (get (first values) "year")))]
       ... ) ; Use key here
    
  • (loop [values map ...]也会导致错误。 map是一个函数,因此抛弃传递给most-falls的参数,用map函数覆盖它。当您尝试将values用作序列时,这会导致错误,因为map函数不支持empty?first,或者您正在尝试的任何其他内容用它来做。我想你只是想重新绑定在循环中使用的参数。只需将其更改为(loop [values values ...]即可。可以说,你不应该通过创建具有相同名称的其他绑定来影响参数,但这在这里并不是非常重要。

还有一些其他小事可以改进。通过使用解构,您可以跳过对firstrest的调用,使用reduce可以使用loop简化显式循环,但这些会减损主要问题。考虑到我上面提到的内容,我将你的函数编写为:

(defn most-falls [values]
  (loop [values values
         count-tracker {}]
    (if (empty? values)
      count-tracker
      (let [key (keyword (get (first values) "year"))]
        (recur (rest values)
               (if (contains? (first values) key)
                 (update count-tracker key inc)
                 (assoc count-tracker key 1)))))))

答案 1 :(得分:1)

首先,该条款肯定存在问题:

(loop [values map count-tracker{}]

我没有使用你想要达到的目标,但请看一下。

接下来,永远不要在代码中使用defdefn表单,只能在命名空间的顶层使用。

最后,loop/recur是一种非常低级的形式,应该充分了解你在做什么。更常见的是,它可能会被更加用户友好的用户所取代。我相信reduce会很好。它需要一个初始值(在你的情况下是一个空映射),一个集合和两个参数的函数,其中第二个是当前集合的项目,第二个是初始值或前一个函数的结果调用

在该函数中,您可以决定地图是否具有某个特定键,如果不是,则添加它。

简短的例子:

# here are some data you've read from a file
(def items [{...} {...} {...}])

# reduce process function
(defn process
  [result item]
  (if (:some-key result) ;; here, you check the current map for a key
    result               ;; return the old map if everything is ok
    (assoc result :some-key some-data)))  ;; accumulate a new key into a map

(reduce process {} items)