![](/img/trans.png)
[英]Clojure: How to apply a function to every value in a nested map and update?
[英]In Clojure, How do I update a nested map correctly?
經過多年的Java(和PHP / JavaScript)經驗,我才剛剛開始學習Clojure。 多么挑戰:-)
如何慣用地更新值映射? 當我在map
上使用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)
}
))
我已使用字符串鍵將任務放在哈希圖中,以便可以快速,直接地訪問映射中的任何任務。 每個任務也都具有鍵,因此我知道將單個任務傳遞給其他功能時的關鍵。
為了更新持續時間,我使用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))
)
我用這個來打印之前/之后的值:
(println "ORIGINAL tasks:")
(println @tasks)
(swap! tasks update-task-durations)
(println "\nUPDATED tasks:")
(println @tasks)
1)我遇到的主要問題是map
函數返回一個序列,而不是一個映射,因此我不得不使用into {}
重新將序列轉換回map,這在我看來似乎是不必要且效率低下的。
有一個更好的方法嗎? 我應該使用map
以外的功能嗎?
我可以更好地安排我的數據結構,同時仍然可以直接訪問單個任務嗎?
是否可以使用into {}
將(可能非常大的)序列轉換為圖譜?
2)另外,在我傳遞給map
函數的函數參數中,當我希望有一個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)
對於Clojure <1.7,請使用(update-in % [:duration] ...
另外,您也可以使用分解來簡化當前解決方案,而無需定義實用程序功能:
(->> tasks
(map (fn [[k task]]
[k (update task :duration update-duration)]))
(into {})
map
僅處理序列。 如果您對類型簽名感興趣,這意味着map
總是具有相同的類型( map :: (a -> b) -> [a] -> [b]
),但這也意味着您將一勞永逸map
是一連串的事。
map
在執行任何操作之前先對其集合參數調用seq
對序列進行seq
-ing會給您一系列鍵-值對。
在這里不必太擔心效率。 into
速度很快,這很慣用。
只需獲得更多選擇即可:您可以使用for
代替map
(into {}
(for [[key value] your-map]
[key (do-stuff value)]))
更快的方法是reduce-kv
(reduce-kv
(fn [new-map key value]
(assoc new-map key (do-stuff value)))
{}
your-map))
當然你也可以使用簡單的reduce
(reduce (fn [m key]
(update m key do-stuff))
your-map
(keys your-map))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.