【发布时间】:2018-11-08 00:36:34
【问题描述】:
这个问题真的很长;我欢迎 cmets 为这个问题建议更好的论坛。
我正在为swarming behavior of birds 建模。为了帮助我整理思路,我创建了三个协议来代表我看到的主要领域概念:Boid、Flock(boid 集合)和Vector。
当我仔细考虑时,我意识到我正在创建新类型来表示 Boid 和 Flock,而这些类型可以使用规范的地图非常清晰地建模:boid 是位置和速度的简单地图(两个向量),flock 是 boid 贴图的集合。干净、简洁、简单,并消除了我的自定义类型,以支持地图和clojure.spec 的所有功能。
(s/def ::position ::v/vector)
(s/def ::velocity ::v/vector)
(s/def ::boid (s/keys ::position
::velocity))
(s/def ::boids (s/coll-of ::boid))
但是,虽然 boid 可以很容易地表示为一对向量(并且一群可以表示为 boid 的集合),但我不知道如何对向量进行建模。我不知道我是否想使用笛卡尔坐标或极坐标来表示我的向量,所以我想要一个可以让我抽象出这些细节的表示。无论我如何在后台存储向量分量,我都想要一个基本的向量函数代数。
(defprotocol Vector
"A representation of a simple vector. Up/down vector? Who cares!"
(magnitude [vector] "Returns the magnitude of the vector")
(angle [vector] "Returns the angle of the vector (in radians? from what
zero?).")
(x [vector] "Returns the x component of the vector, assuming 'x' means
something useful.")
(y [vector] "Returns the y component of the vector, assuming 'y' means
something useful.")
(add [vector other] "Returns a new vector that is the sum of vector and
other.")
(scale [vector scaler] "Returns a new vector that is a scaled version of
vector."))
(s/def ::vector #(satisfies? Vector %))
除了一致性美学之外,这种差异困扰我的最大原因是生成式测试:我还没有做过,但我很高兴学习,因为一旦我指定了它,它就可以让我测试我的更高级别的函数我的低级原语。问题是,如果不将抽象协议/规范耦合到定义功能的具体记录,我不知道如何为::vector 规范创建生成器。我的意思是,我的生成器需要创建一个Vector 实例,对吗?要么我 proxy 就在生成器中,所以创建一个不必要的 Vector 实现只是为了测试,或者我将我很好的抽象协议/规范耦合到一个具体的实现。
问题:如何使用规范对向量(其中行为集比特定数据表示更重要的实体)建模?或者,如何在不将规范与具体实现绑定的情况下为基于协议的规范创建测试生成器?
更新 #1: 以不同的方式解释它,我创建了一个分层数据模型,其中特定层仅根据其下方的层来编写。 (这里没什么新奇的。)
Flock (functions dealing with collections of boids)
----------------------------------------------------
Boid (functions dealing with a single boid)
----------------------------------------------------
Vector
由于这个模型,删除所有更高的抽象将使我的程序变成向量操作。这一事实的一个理想推论:如果我能找到一个向量生成器,我就可以免费测试我所有的高级抽象。那么如何指定 Vector 并创建合适的测试生成器?
显而易见但不充分的答案:创建一个规范 ::vector,它表示一对坐标的映射,例如 (s/keys ::x ::y)。但是为什么(x, y)?如果我可以访问(angle, magnitude),一些计算会更容易。我可以创建 ::vector 来表示一对坐标,但是那些想要 other 表示的函数必须知道并关心向量是如何在内部存储的,因此必须知道如何进行外部转换功能。 (是的,我可以使用 multispec/conform/multimethods 来实现这一点,但使用这些工具听起来像是一种不必要的泄漏抽象;我不希望更高的抽象知道或关心向量可以以多种方式表示。)
更基本的是,向量不是(x, y) 或(angle, magnitude),它们只是“真实”向量的投影,但是您想定义它。 (我说的是域建模,而不是数学严谨性。)因此,在这种情况下,创建一个将向量表示为一对坐标的规范不仅是一个糟糕的抽象,而且它并不代表域实体。
更好的选择是我上面定义的协议。所有更高的抽象都可以用Vector 协议编写,给我一个干净的抽象层。但是,如果不将我的抽象与具体实现结合起来,我就无法创建一个好的Vector 测试生成器。也许这是我必须做出的权衡,但有没有更好的方法来建模呢?
【问题讨论】:
-
这个问题我不是很清楚,好像同时问了很多东西。我什至会争辩说这有点“复杂”;-) 所以我读到你有某种向量和(1)。您需要多态行为,而现有的机制(协议和多方法)都不足以解决这个问题:为什么? (2) 您希望在不将规范与具体实现绑定的情况下对向量进行生成测试。
-
#1 的答案是#2:我会使用协议,除非我没有将我的抽象(规范/协议)耦合到具体实现的情况下进行生成测试。我会更新这个问题,让它更清楚一点。
-
太好了,谢谢!我可能知道你怎么做,会看到......
标签: clojure clojure.spec