由于我正在学习西班牙语,此刻我正在做一个死的简单快闪程序。
该应用程序有两个概念:
["spanish" "verb"]
。该应用程序的工作原理简单地通过选择一个配置文件来练习,让你在卡的正面与最低知识得分。当用户就绪时,它显示了背面。当用户输入他是否记得卡,修改该卡的知识得分。
对于任何人任何快闪程序之前使用它,这是超级平凡的东西。
我的问题是:如何在Clojure中惯用的模型呢?我在得到所面临的挑战是型材和卡之间的许多一对多的关系。
我可以创建一个状态图是这样的:
{:card-universe [
{:front "Correr" :back "To run" :tags ["spanish" "verb"]}
{:front "Querer" :back "To want" :tags ["spanish" "verb"]}
{:front "La mesa" :back "The table" :tags ["spanish" "noun"]}]
:profiles [
{
:name "Spanish verbs"
:tags ["spanish" "verb"]
:cards [{:front "Correr" :back "To want" :score 7}
{:front "Querer" :back "To want" :score 10}]
}
{
:name "Spanish"
:tags ["spanish"]
:cards [{:front "Correr" :back "To run" :score 8}
{:front "Querer" :back "To want" :score 3}
{:front "La mesa" :back "The table" :score 2}]
}
]
}
这对我来说看起来很可笑。说我编辑卡,因为我犯了一个错误,那我就必须去通过所有的配置文件和更新它们。我可以对所有卡创建身份解决这个问题(有点),只是用它来指证,而不是:
{:card-universe [
{:id "c1" :front "Correr" :back "To run" :tags ["spanish" "verb"]}
{:id "c2" :front "Querer" :back "To want" :tags ["spanish" "verb"]}
{:id "c3" :front "Mesa" :back "Table" :tags ["spanish" "noun"]}]
:profiles [
{
:name "Spanish verbs"
:tags ["spanish" "verb"]
:cards [{:id "c1" :score 7}
{:id "c2" :score 10}]
}
{
:name "Spanish words"
:tags ["spanish"]
:cards [{:id "c1" :score 8}
{:id "c2" :score 3}
{:id "c3" :score 2}]
}
]
}
这也许是一个更好一点,但它仍然意味着,如果我在一个给定的标签添加更多的卡,我将不得不获取所有的牌。卡宇宙和:卡在我的个人资料之间基本上是一个外连接。
弹出下一个问题就是存储状态。我当然可以此状态下进行右只存储一个文件,但如果我是通过创建Web应用程序的SQL数据库将是我去将其扩展到多用户。在我心目中,我应该能全部达代码这一点,并存储在开始一个文件,以后还可以换出我如何存储数据,而无需触摸应用程序使用的功能的数据结构。
任何提示和经验将不胜感激!
我有一种感觉,该应用程序过于简单得到任何的好处Clojure的。特别是引入数据库时 - 这将基本上只是使这是一个CRUD应用程序。
我可能会采取先东西拆开一点开始
(def card-data
[{:id "c1" :front "Correr" :back "To run" :tags #{"spanish" "verb"}}
{:id "c2" :front "Querer" :back "To want" :tags #{"spanish" "verb"}}
{:id "c3" :front "Mesa" :back "Table" :tags #{"spanish" "noun"}}])
(defn spanish-words [cards]
(filter #(-> % :tags (every? ["spanish"])) cards))
(defn spanish-verbs [cards]
(filter #(-> % :tags (every? ["spanish" "verb"])) cards))
然后做一个用于测试的小原子分贝,与可以在其中存储状态的功能。你可以在以后你最终使用任何DB此功能抽象。
(def db (atom {}))
(defn remembered! [scores-db card]
(swap! scores-db update (:id card) #(if % (inc %) 0)))
现在,我们可以对其进行测试。
#_user=> (->> card-data spanish-verbs first (remembered! db))
{"c1" 0}
#_user=> (->> card-data spanish-verbs second (remembered! db))
{"c1" 0, "c2" 0}
#_user=> (->> card-data spanish-verbs first (remembered! db))
{"c1" 1, "c2" 0}
这样可行。但是,我们可以进一步抽象出我们的过滤成select-tags
功能。
(defn select-tags [cards & tags]
(filter #(-> % :tags (every? (->> tags flatten (remove nil?)))) cards))
(defn spanish [cards & tags]
(select-tags cards "spanish" tags))
(defn verbs [cards & tags]
(select-tags cards "verb" tags))
#_user=> (spanish (verbs card-data))
({:id "c1", :front "Correr", :back "To run", :tags #{"verb" "spanish"}} {:id "c2", :front "Querer", :back "To want", :tags #{"verb" "spanish"}})
#_user=> (verbs (spanish card-data))
({:id "c1", :front "Correr", :back "To run", :tags #{"verb" "spanish"}} {:id "c2", :front "Querer", :back "To want", :tags #{"verb" "spanish"}})
而现在,我们只需撰写他们。
(defn spanish-verbs [cards & tags]
((comp spanish verbs) cards tags))
;; or (apply spanish cards "verb" tags)
;; or even (apply select-tags cards "verb" "spanish" tags)
#_user=> (->> card-data spanish-verbs first (remembered! db))
{"c1" 2, "c2" 0}
如果你熟悉SQL,你应该适宜步行的SQL库和SQLite马上开始:http://walkable.gitlab.io您将大大受益于SQL的规范化中获益。步行的将仅仅通过几个按键使读取数据为树形结构微风,具有过滤。不要浪费你的时间打一场原子,域并不复杂,它不值得花时间做CRUD原型。