如何在 cljs re-frame 中创建自定义的 “getter” 查询?

huangapple go评论58阅读模式
英文:

How to create a custom "getter" query in cljs re-frame?

问题

我正在编写一个使用 re-frame 的应用程序,模拟一个棋盘游戏,其中包含一个包含单元格数组的棋盘结构,类似于:

{... :board-cells [{:name "cell-1" :material #object} {:name "cell-2" :material #object} ...]}

虽然 re-frame 支持使用像 (db :board-cells) 这样的漂亮关键字语法获取 "自然" 子结构,但我厌倦了每次想获取材料时都要编写整个 "深入查询"。像 (get (nth (db :board-cells) index) :material)。这还有一个缺点,即将我的数据库的物理布局与应用程序逻辑紧密耦合。如果我决定更改数据库结构怎么办?那么我必须更新十个不同的地方而不仅仅是一个。

是否有 re-frame 官方的方法来创建一个 "虚拟查询",以便我可以使用类似 (db :get-nth-mat n) 的方式获取材料,其中 n 是 board-cells 数组中的单元格编号?我认为 db.cljsreg-sub 是我可以做到这一点的地方,但似乎不起作用。是的,我可以创建自己的 getter:

(defn get-material [db index]
  (get (nth (db :board-cells) index) :material))

并像 (println "mat-4=" (cell/get-material db 4)) 这样调用它,但这不如 (db :get-nth-mat n) 方便或漂亮。

非常感谢。

英文:

I'm writing a re-frame app that models a board game where I have a board structure containing an array of cells, something like:

{... :board-cells [{:name "cell-1" :material #object} {:name "cell-2" :material #object} ...]}

While re-frame supports the getting of "natural" substructures with a nice keyword syntax like (db :board-cells), I'm getting tired of having to write the entire "drill-down" query every time I want to get a material: (get (nth (db :board-cells) index) :material). This also has the downside of tightly coupling the physical layout of my db to my application logic. What if I decide to change my db structure? Then I have to update ten different spots instead of just one.

Is there a re-frame official way to create a "virtual query" so I can get a material with something like (db :get-nth-mat n), where n is the cell number within the board-cells array? I thought that db.cljs and reg-sub was where I could do this, but it doesn't seem to work. Yes, I can create my own getter:

(defn get-material [db index]
  (get (nth (db :board-cells) index) :material))

and call it like (println "mat-4=" (cell/get-material db 4)), but this isn't as convenient or nice as (db :get-nth-mat n)

Many thanks.

答案1

得分: 1

db 只是一个映射,与 re-frame 没有任何关系,但每个映射都是一个函数,关键字也是如此。所以当你执行 (map something)(:keyword something) 时,实际上在执行 (get map something)(get something :keyword)

因此,除了以不同方式访问/迭代数据(例如 doseqformap 等)之外,实际上没有“快捷方式” - 假设你打算逐个单元格地渲染网格;这样你就完全摆脱了基于索引的访问。

否则,我会使用类似你的专用函数,但更愿意将其命名为 material-by-idx(在面向对象编程中命名函数为 getset 类似访问器是相当不常见的(但也有适用的地方,例如用于状态修改的 set)。

拥有适当命名、理想情况下是纯函数,只做一件事情的函数,是函数式编程和 Lisp 编程的重要基石。通常,必须多输入一些代码的缺点可以通过更高级的编程范式,如线程化或部分应用,或作为最后的手段,宏来缓解。

你也可以使用 get-in 来稍微简化它:

(defn material-by-idx [db idx]
  (get-in db [:board-cells idx :material]))

例如,在你的循环中,如果你认为有价值,你现在可以使用类似这样的方式:

(let [mat-at (partial material-by-idx db)]
  (mat-at 5))

顺便说一句:你希望的版本 (db :get-nth-mat n) 实际上是可以工作的(但不是你希望的方式)。它会转换成 (get db :get-nth-mat n)(3个参数的 get),如果 db 中没有键 :get-nth-mat,它会返回 n

英文:

db is just a map and this "feature" there has nothing to do with
re-frame, but every map is a function and so are keywords. So when you
do (map something) or (:keyword something) you are actually doing
(get map something) and (get something :keyword).

So there really is no "shortcut" other than accessing/iterating your
data differently (e.g. doseq, for, map, ...) - assuming you are
about to render the grid cell by cell; This way you get rid of the
index based access at all.

Otherwise I'd use a dedicated function like yours, but would rather name
it material-by-idx (it's rather uncommon to name function get and
set like accessors in OO (but there are places for it like e.g.
set for state modification)).

Having properly named, ideally pure, functions, that do one thing
properly is an important building block in functional and Lisp
programming. And often the downside of having to type a bit more can be
mitigated by higher level programming paradigms like threading or partial
application or as last resort, macros.

And you can use get-in to unclutter it a bit:

(defn material-by-idx [db idx]
  (get-in db [:board-cells idx :material]))

E.g. you could now in your loop use something like this, if you see
value in that:

(let [mat-at (partial material-by-idx db)]
  (mat-at 5))

Btw: The version you are wishing for (db :get-nth-mat n) actually
works (but not as you wish for). It turns into (get db :get-nth-mat
n)
(3 argument get), which returns you n if there is no key
:get-nth-mat in db.

huangapple
  • 本文由 发表于 2020年1月3日 13:33:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/59573644.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定