Ste*_*ley 5 dictionary clojure update-in
经过多年的Java(和PHP/JavaScript)经验,我刚刚开始学习Clojure.真是个挑战:-)
如何以惯用方式更新值的映射?当我map在地图上使用该函数时,它不返回地图,它返回一个序列.
我正在开发一个小应用程序,我有一个任务列表.我想做的是改变一些单独任务中的一些值,然后更新原始任务列表.以下是我正在测试的任务:
(defrecord Task [key name duration])
(def tasks
(atom
{
"t1" (->Task "t1" "Task 1" 10)
"t2" (->Task "t2" "Task 2" 20)
"t3" (->Task "t3" "Task 3" 30)
}
))
Run Code Online (Sandbox Code Playgroud)
我已经使用字符串键将任务放在散列映射中,因此它可以快速,直接地访问映射中的任何任务.每个任务都包含密钥,因此我知道当我将各个任务传递给其他函数时,关键是什么.
更新我正在使用的持续时间,map并update-in迭代并有选择地更新每个任务的持续时间,并返回修改后的任务.
这是功能:
(defn update-task-durations
"Update the duration of each task and return the updated tasks"
[tasks]
; 1) Why do I have to convert the result of the map function,
; from a sequence then back to a map?
(into {}
(map
(fn [task]
(println task) ; debug
(update-in
task
; 2) Why do I have to use vector index '1' here
; to get the value of the map entry?
[1 :duration]
(fn [duration]
(if (< duration 20)
(+ duration 1)
(+ duration 2)
)
)
)
) tasks))
)
Run Code Online (Sandbox Code Playgroud)
我用这个打印之前/之后的值:
(println "ORIGINAL tasks:")
(println @tasks)
(swap! tasks update-task-durations)
(println "\nUPDATED tasks:")
(println @tasks)
Run Code Online (Sandbox Code Playgroud)
1)我遇到的主要问题是map函数返回一个序列,而不是一个map,所以我不得不再次将序列转换回一个map,into {}这对我来说似乎是不必要和低效的.
有一个更好的方法吗?我应该使用除以外的功能map吗?
我可以更好地安排我的数据结构,同时仍然可以直接访问单个任务吗?
可以使用into {}?将(可能非常大的)序列转换为地图吗?
2)另外,在我的函数参数中,我传递给map函数,每个任务都给我map,作为表格的向量,[key value]当我期望一个地图条目时,所以从我有的地图条目中获取值将以下键传递给我update-in [1 :duration]这看起来有点难看,是否有更好/更清晰的方式来访问映射条目而不是使用向量的索引1?
解决映射映射问题的一种流行方法是zipmap:
(defn map-vals
"Returns the map with f applied to each item."
[f m]
(zipmap (keys m)
(map f (vals m))))
(defn update-task-durations
[tasks]
(let [update-duration (fn [duration]
(if (< duration 20)
(+ 1 duration)
(+ 2 duration)))]
(->> tasks
(map-vals #(update % :duration update-duration)))))
(swap! tasks update-task-durations)
Run Code Online (Sandbox Code Playgroud)
对于 Clojure < 1.7,请改用(update-in % [:duration] ...。
或者,您也可以使用解构来简化当前的解决方案,而无需定义实用函数:
(->> tasks
(map (fn [[k task]]
[k (update task :duration update-duration)]))
(into {})
Run Code Online (Sandbox Code Playgroud)
map只处理序列。如果您热衷于类型签名,这意味着map始终具有相同的类型 ( map :: (a -> b) -> [a] -> [b]),但这也意味着您将得到的只是map一个 seq-of-something。
map在执行任何操作之前调用seq其集合参数,并且seq-ing 映射会为您提供一系列键值对。
这里不必太担心效率。into速度很快,而且这是非常惯用的。