1

我是 clojure 的新手,并且正在通过从 Practical Common Lisp 重新创建 cd 项目来学习。我在使用 where 'clause' 选择器实现更新功能时遇到了麻烦。执行 (update (where :artist "AC/DC") :rating 10) 导致我的数据库被炸毁,只返回一个空列表。这是我的代码:

(defn where [& {:keys [title artist genre rating]}]
(fn [cd]
    (and
        (if title (= (get cd :title) title) true)
        (if artist (= (get cd :artist) artist) true)
        (if genre (= (get cd :genre) genre) true)
        (if rating (= (get cd :rating) rating) true))))

(defn update [selector-fn & {:keys [title artist genre rating]}]
    (def ^:dynamic *db*
        (map (fn [row]
            (when (apply selector-fn row)
                    (if title (def row (assoc-in row [:title] title)))
                    (if artist (def row (assoc-in row [:artist] artist)))
                    (if genre (def row (assoc-in row [:genre] genre)))
                    (if rating (def row (assoc-in row [:rating] rating))))
            row)
        *db*)))

我已将每张 CD 实现为哈希映射:

(defn make-cd [title artist genre rating]
{:title title
:artist artist
:genre genre
:rating rating
})

所以我认为我对 assoc-in 的使用是正确的。任何关于我做错了什么的想法将不胜感激。

谢谢...

美中

好的。根据 Arthur 的评论,我现在拥有的更新功能是:

(defn update [selector-fn & {:keys [title artist genre rating]}]
        (map (fn [row]
            (when (apply selector-fn row)
                (-> row
                    (#(if title (assoc-in % [:title] title) %))
                    (#(if artist (assoc-in % [:artist] artist) %))
                    (#(if genre (assoc-in % [:genre] genre) %))
                    (#(if rating (assoc-in % [:rating] rating) %)))))
        *db*))

我想我仍然需要map表单,因为我需要遍历*db*. 我不想只更改*db*艺术家为 AC/DC 的所有 CD 的评级。somap将遍历每个 cd (将其绑定到row),然后调用该where函数以查看标题是否匹配。如果是这样,那么它将返回 true 以允许更新评级。

不幸的是,这仍然不起作用。

ArityException Wrong number of args (4) passed to: core$where$fn  clojure.lang.AFn.throwArity (AFn.java:437)
4

2 回答 2

3

在函数中使用def很少有预期的结果(并且不是线程安全的)。因为每个操作都需要一个映射,对其进行更改并返回已更改的映射,因此您可以将每个操作线程化到下一个操作中,以便最后一个的返回值是所有更改的效果。这种模式在 Clojure 中非常常见,以至于有一个两个字符的宏可以非常方便地做到这一点:

user> (-> {:a {:k 1} :b {:k 2} :c {:k 3}} 
          (assoc-in [:a :k] 8) 
          (assoc-in [:b :k] 9))
{:a {:k 8}, :c {:k 3}, :b {:k 9}} 

“线程优先”maco->只是将每个表达式作为第一个参数插入到下一个。所以上面的表达式(带有一些艺术许可)扩展为:

(assoc-in (assoc-in {:a {:k 1} :b {:k 2} :c {:k 3}} [:a :k] 8) [:b :k] 9)) 

在您的上下文中,这可能看起来像这样:

(-> row
    (#(if title (assoc-in % [:title] title) %))
    (#(if artist (assoc-in % [:artist] artist) %))
    (#(if genre (assoc-in % [:genre] genre) %))
    (#(if rating (assoc-in % [:rating] rating) %)))

每行创建一个函数,该函数要么返回其参数的更改版本,要么原样返回,然后调用它。参数将row插入到行尾的两个括号之间。如果这在视觉上不吸引人,您可以为函数命名defn并仅列出->调用中的名称。

于 2012-10-29T18:44:18.180 回答
0

在我看来,您正试图在更新函数中改变行,但您真正要做的是在每一步中重新定义它。

但是您的map函数没有返回新行。

我会reduce在指定时更改行本身的属性。

于 2012-10-29T18:40:09.247 回答