Clojure: моделирование простых отношений «многие ко многим» - PullRequest
0 голосов
/ 03 февраля 2019

Поскольку я изучаю испанский в настоящий момент, я делаю простейшее простое приложение Flashcard.

Приложение имеет две концепции:

  1. Сами карты.Две струны, одна спереди и одна сзади.Кроме того, каждая карта помечена 0-м тегами.Например, теги для данной карты могут быть ["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}]
  }
 ]
}

Это может быть немного лучше, но это все равно будет означать, что если ядобавить больше карт в данный тег, я должен был бы получить все карты.По сути, это внешнее соединение между my: card-universe и: cards в профиле.

Следующий всплывающий вопрос - сохранение состояния.Конечно, я мог бы просто сохранить это состояние в файле, но если бы я расширил его до многопользовательского, создав веб-приложение, я бы использовал базу данных SQL.По моему мнению, я должен быть в состоянии закодировать все это и сохранить в файле в начале, а затем уметь поменять способ хранения данных, не затрагивая структуру данных, которую приложение использует для работы.

ЛюбойСоветы и опыт будут с благодарностью!

У меня такое ощущение, что приложение слишком простое, чтобы получить какие-либо преимущества Clojure.Особенно при представлении базы данных, которая в основном просто превращает это приложение в CRUD.

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

если вы знакомы с SQL, вам следует сразу начать с библиотеки Walkable sql и sqlite: http://walkable.gitlab.io Вы получите большую пользу от нормализации SQL.Walkable сделает выборку данных в виде древовидной структуры быстрым, с фильтрацией всего несколькими нажатиями клавиш.Не тратьте свое время на борьбу с атомом, домен не сложен, не стоит тратить время на создание прототипов CRUD.

0 голосов
/ 03 февраля 2019

Я бы, пожалуй, сначала немного разбирал вещи

(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}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...