如何使有状态的 Clojure 代码变得纯净而不过度重复?

问题描述 投票:0回答:1

我正在编写一个 Clojure 库来使用四边数据结构生成 Voronoi 图,如本研究论文中所述。我使用 refs 来表示边缘记录,但我想将代码更改为纯粹的,原因如下:

  • 我不希望库的用户不得不担心细节,例如将代码包装在
    dosync
    中。
  • 我希望能够进行非破坏性更新,例如将图表剪切到视口而不改变原始三角测量。
  • Clojurescript 中未实现引用。

问题在于,由于将数据结构传递给每个函数,纯代码极其重复。这是一个例子:

(defn- bubble-up [qeds cross]
  (loop [qeds qeds
         cross cross]
    (let [l-edge (first-hit qeds cross (q/r-prev qeds cross) q/o-next)
          r-edge (first-hit qeds cross (q/o-prev qeds cross) q/o-prev)
          l-valid (above? qeds l-edge cross)
          r-valid (above? qeds r-edge cross)]
      (when (or l-valid r-valid)
        (let [[qeds new-cross]
              (if (or (not l-valid)
                      (and r-valid
                           (in-circle? (dest qeds l-edge) (org qeds l-edge) (org qeds r-edge)
                                       (dest qeds r-edge))))
                (connect qeds r-edge (q/sym cross))
                (connect qeds (q/sym cross) (q/sym l-edge)))]
          (recur qeds new-cross))))))

我尝试过各种其他方法,包括:

  • 用Java编写可变部分。
  • 使用状态单子(稍微好一点,但仍然笨重且不惯用。)
  • 将数据结构放入动态变量中。

在 Clojure(script) 中是否有一个标准的、惯用的方法来处理这样的情况?

clojure state clojurescript voronoi delaunay
1个回答
0
投票

在 Clojure(script) 中是否有一个标准的、惯用的方法来处理这样的情况?

无法对所有人做出判断,但我会让事情保持原样。 并且冒着陈述显而易见的风险,如果可以在超过 2 个位置看到某些特定的形式集(不仅仅是单个函数调用),那么它可能应该被提取到自己的函数中。但所提供的代码没有类似的东西。

如果您不喜欢这种重复,以至于必须做一些事情,作为替代方案,您可以创建一个类似于 partial 但颠倒过来的函数:

(defn bind [obj & fns]
  (mapv #(prepare % obj) fns))

然后将其用作:

(let [[first-hit r-prev o-prev above?] (bind qeds first-hit o/r-prev o/o-prev above?)] l-edge (first-hit cross (r-prev cross) q/o-next) r-edge (first-hit cross (o-prev cross) q/o-prev) l-valid (above? l-edge cross) r-valid (above? r-edge cross)] ...)

值不值得,就看你自己了。我可能不会这样做,除非有更多类似的行重复使用相同的功能。

作为另一种选择,您可以使用动态变量来存储图形。但在某些方面,它可能比基于 refs/atoms 的解决方案更糟糕。

© www.soinside.com 2019 - 2024. All rights reserved.