Clojure在将匿名函数与map一起使用时遇到的问题

Clojure在将匿名函数与map一起使用时遇到的问题,clojure,Clojure,我是clojure的新手,通过从实用的CommonLisp重新创建cd项目来学习。使用where“子句”选择器实现更新函数时遇到问题。执行(update(其中:artist“AC/DC”):rating 10)会导致我的数据库崩溃,只返回一个空列表。这是我的密码: (defn where [& {:keys [title artist genre rating]}] (fn [cd] (and (if title (= (get cd :title) title)

我是clojure的新手,通过从实用的CommonLisp重新创建cd项目来学习。使用where“子句”选择器实现更新函数时遇到问题。执行(update(其中: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是正确的。任何关于我做错了什么的想法都将不胜感激

谢谢

MZ

嗯。根据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*))
我想我仍然需要
映射
表单,因为我需要迭代
*db*
。我不想更改
*db*
中所有CD的评级,只想更改艺术家为AC/DC的CD。因此,
map
将迭代每个cd(将其绑定到
),然后调用
where
函数查看标题是否匹配。如果是,那么它将返回true,允许更新评级

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

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

在我看来,您试图在update函数中改变行,但您真正做的是在每个步骤中重新定义它

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


我会
减少
行本身,在指定属性时更改属性。

在函数中使用
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
->
只是将每个表达式作为第一个参数插入下一个参数。因此,上述表述(带有一些艺术许可)扩展为:

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

(-> 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) %)))

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

确定。我想我现在明白了。我删除了“apply”表单,只调用选择器fn like(选择器fn行)。它正在返回更新的cd。不幸的是,它还将db中的其余CD返回为nil。然而,这是进步!我只需要弄清楚如何正确地更新数据库。谢谢你的帮助!
(-> 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) %)))