我正在尝试使用循环重复,在循环内创建一个空映射。对于循环中的每个条目(循环遍历映射向量),它将查看新创建的映射中是否存在与迭代值键匹配的键,如果没有则创建一个键。
我创建了这段代码:
(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我知道这个问题有点含糊,所以任何问题都要问!
答案 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 ...]
即可。可以说,你不应该通过创建具有相同名称的其他绑定来影响参数,但这在这里并不是非常重要。
还有一些其他小事可以改进。通过使用解构,您可以跳过对first
和rest
的调用,使用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{}]
我没有使用你想要达到的目标,但请看一下。
接下来,永远不要在代码中使用def
或defn
表单,只能在命名空间的顶层使用。
最后,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)