与地图一起使用匿名函数的Clojure问题

时间:2012-10-29 18:14:28

标签: clojure

我是clojure的新手,我正在通过从Practical Common Lisp重新创建cd项目来学习。我在使用where'子句'实现更新功能时遇到了麻烦。选择。执行(更新(其中:艺术家" AC / DC"):评级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的使用是正确的。关于我做错的任何想法都将不胜感激。

感谢...

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*))

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

不幸的是,这仍然无效。

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

2 个答案:

答案 0 :(得分: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参数将插入到行尾的两个parens之间。如果这不具有视觉吸引力,您可以使用defn为功能命名,并仅列出->来电中的名称。

答案 1 :(得分:0)

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

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

我希望reduce行本身在指定时更改属性。