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

问题描述 投票:0回答:2

我试图使用循环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我知道这个问题有点模糊所以任何问题只是问!

json clojure functional-programming atom-editor
2个回答
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
投票

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

(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)
© www.soinside.com 2019 - 2024. All rights reserved.