(def nextStudentNumber 1000)
(defn s
[lastName firstName]
(let [student {:nextStudentNumber {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}}]
在这种情况下,我已经创建了 var nextStudentNumber
,我想映射在学生身上更改的键。
是的,像任何对象一样,var 可以是映射中的键:
{#'foo 42}
=> {#'user/foo 42}
由于这对您没有帮助,因此以下是您的问题的示例构造:
(->> [{:name "Mara"} {:name "Lara"} {:name "Clara"}]
(map-indexed #(assoc %2 :id %1)) ;; assign an ID to each student
(group-by :id)) ;; Create a hash-map where students can be looked up by ID
=> {0 [{:name "Mara, :id 0}], 1 [{:name "Lara", :id 1}]
2 [{:name "Clara", :id 2}]}
请注意,此示例有点多余(因为可以通过使用索引调用它直接在向量中执行索引查找(。
但是,它显示了使用不可变数据的基本思想:在独立的步骤中,您可以生成 ID 并进行分组。您可以通过多种方式编写此类步骤,例如为新学生生成 ID、将他们添加到现有学生的集合中以及按 ID 对他们进行分组。 请注意,(group-by :id)
步骤独立于 ID 的生成方式工作,并容忍两个学生具有相同 ID 的情况,然后您可以检测和处理这些 ID。
命令式方法在功能性Clojure世界中非常不典型。如果你有很多分配学生的函数(在你的思维中应该调用s
(,而是让他们纯粹,让他们返回学生的集合,给他们起名字,比如load-students-from-file
,generate-ids
并使用像concat
这样的结构,into
组合他们返回的集合,而不需要弄乱状态, 直到您对学生数据执行 I/O。
如果出于好奇,你仍然想弄乱状态,这里有另一个(相当不习惯的(代码示例:
(def student-count (atom 0))
(def students-by-id (atom {}))
(defn add-student! [first-name last-name]
(let [next-id (swap! student-count inc)]
(swap! students-by-id
assoc next-id {:first-name first-name, :last-name last-name
:id next-id})))
例如:
(add-student! "Mara" "Mad")
(add-student! "Lara" "Light")
(@students-by-id 1)
=> {:first-name "Lara", :last-name "Light", :id 1}
如您所见,这里一切都
{:nextStudentNumber {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}
我假设您希望将其转换为以下内容:
{:1000 {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}
在这种情况下,您需要:
{(keyword (str nextStudentNumber)) {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}
还有其他一些臭的东西,如骆驼案和inc
.我们在 clojure 中使用悬浮蛇lastName
这样last-name
.我不知道你在用inc
做什么,但它散发出沉重的命令式共鸣。