我试图使用循环recur在循环内创建一个空映射。对于循环中的每个条目(循环遍历映射向量),它将查看新创建的映射中是否存在与迭代值键匹配的键,如果不创建一个键。
我创建了这段代码:
(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我知道这个问题有点模糊所以任何问题只是问!
这里有很多东西要提出来。你的主要问题是与(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)))))))
首先,该条款肯定存在问题:
(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)