【问题标题】:Clojure: Modeling simple many to many relationshipClojure:建模简单的多对多关系
【发布时间】:2019-02-07 19:29:46
【问题描述】:

因为我现在正在学习西班牙语,所以我正在制作一个非常简单的抽认卡应用程序。

应用程序有两个概念:

  1. 卡片本身。两根弦,一根在前面,一根在后面。此外,每张卡片都标有 0-m 标签。例如。给定卡片的标签可以是["spanish" "verb"]
  2. 配置文件。配置文件存储两件事:通过定义标签包含哪些卡片,以及每张卡片的“知识分数”。

该应用程序只需选择要练习的个人资料即可工作,为您提供知识分数最低的卡片正面。当用户准备好时,它会显示背面。然后用户输入他是否记得那张修改了该卡的知识分数的卡。

对于以前使用过任何 Flashcard 应用程序的人来说,这是非常琐碎的东西。

我的问题是:如何在 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}]
  }
 ]
}

这可能会好一点,但这仍然意味着如果我在给定标签中添加更多卡片,我将不得不获取所有卡片。基本上是我的 :card-universe 和配置文件中的 :cards 之间的外连接。

弹出的下一个问题是存储状态。我当然可以将这个状态直接存储到一个文件中,但是如果我要通过创建 Web 应用程序将其扩展到多用户,那么我会选择 SQL 数据库。在我看来,我应该能够在开始时将这一切编码并存储到一个文件中,然后能够在不触及应用程序用于运行的数据结构的情况下交换我存储数据的方式。

任何提示和经验将不胜感激!

我有一种感觉,应用程序太简单了,无法获得 Clojure 的任何好处。尤其是在引入数据库时​​——这基本上只会使它成为一个 CRUD 应用程序。

【问题讨论】:

  • 你打算使用什么样的用户界面?命令行、SPA 网页、普通网页……?

标签: clojure modeling


【解决方案1】:

我可能会先把事情分解一下

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

然后制作一个小原子数据库进行测试,并带有一个可以在其中存储状态的函数。您可以稍后将这个函数抽象到您最终使用的任何数据库上。

(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}

【讨论】:

    【解决方案2】:

    如果您熟悉 SQL,您应该立即开始使用 Walkable sql 库和 sqlite: http://walkable.gitlab.io 您将从 SQL 的规范化中受益匪浅。 Walkable 将使获取数据作为树结构变得轻而易举,只需按几下键即可过滤。不要浪费时间与原子作斗争,领域并不复杂,不值得花时间制作 CRUD 原型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-18
      • 2018-04-20
      • 1970-01-01
      • 2019-07-29
      • 2017-08-25
      • 2014-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多