简体   繁体   English

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

[英]Clojure problems with using anonymous function with map

I am new to clojure, and am learning by recreating the cd project from Practical Common Lisp. 我是clojure的新手,我正在通过从Practical Common Lisp重新创建cd项目来学习。 I am having troubles implementing the update function using a where 'clause' selector. 我在使用where'子句'选择器实现更新功能时遇到了麻烦。 Executing (update (where :artist "AC/DC") :rating 10) results in my database getting blown away, and just an empty list returned. 执行(更新(其中:艺术家“AC / DC”):评级10)导致我的数据库被吹走,只返回一个空列表。 Here is my code: 这是我的代码:

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

I've implemented each CD as a hash-map: 我已将每张CD实现为哈希映射:

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

So am thinking that my use of assoc-in is correct. 所以我认为我对assoc-in的使用是正确的。 Any ideas on what I am doing wrong would be greatly appreciated. 任何关于我做错的想法都将不胜感激。

thanks... 谢谢...

MZ MZ

Ok. 好。 Based on Arthur's comments here is what I have for the update function now: 根据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*))

I think I still need the map form as I need to iterate over *db* . 我想我仍然需要map表格,因为我需要迭代*db* I don't want to change the rating for all the CD's in *db* just the ones where artist is AC/DC. 我不想改变*db*所有CD的评级,只是那些艺术家是AC / DC的CD。 so map will iterate over each cd (binding it to row ), then call the where function to see if the title matches. 因此map将迭代每个cd(将其绑定到row ),然后调用where函数以查看标题是否匹配。 If so, then it will return true allowing the rating to be updated. 如果是这样,那么它将返回true,允许更新评级。

Unfortunately this still doesn't work. 不幸的是,这仍然无效。

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

Using def in a function rarely has the intended result (and is not thread safe). 在函数中使用def很少有预期的结果(并且不是线程安全的)。 Because each operation takes a map, changes it and returns the changed one, you can thread each into the next so that the return value of the last one is the effect of all the changes. 因为每个操作都采用一个映射,更改它并返回更改后的映射,所以您可以将每个操作进入下一个操作,以便最后一个操作的返回值是所有更改的效果。 This pattern is so common in Clojure that there is a two character macro to do it really conveniently: 这种模式在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}} 

the "thread first" maco -> just inserts each expression as the first argument to the next. “thread first”maco ->只是将每个表达式作为第一个参数插入到下一个表达式中。 so the above expression expands (with some artistic license) to: 所以上面的表达式(带有一些艺术许可证)扩展到:

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

In your context this could look something like this: 在您的上下文中,这看起来像这样:

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

each line creates a function that either returns a changed version of it's argument or returns it unchanged, and then calls it. 每一行创建一个函数,该函数返回其参数的更改版本或不更改它,然后调用它。 The row argument will be inserted between the two parens at the end of the line. row参数将插入row的两个parens之间。 If that is not visually appealing you could name the functions with defn and list just the names in the -> call. 如果这不具有视觉吸引力,您可以使用defn命名函数,并仅列出->调用中的名称。

It seems to me you're trying to mutate row inside the update function but what you're really doing is redefining it in every step. 在我看来,你试图在更新函数中改变行,但你真正在做的是在每一步重新定义它。

But your map function is not returning a new row. 但是你的map函数没有返回一个新行。

I would reduce the row in itself changing the properties when specified. 我会reduce行本身在指定时更改属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM