我可以在当前场景中存储多边形的索引,在多边形中存储拖动点的索引,并每次替换它。但是这种方法无法扩展-当成分级别达到5时,样板将变得难以忍受。
完全正确,如果无法绕开样板,这种方法就无法扩展。具体来说,用于创建带有一个小部分的全新场景的样板已更改。但是,许多功能语言提供了一种用于处理这种嵌套结构操纵的构造:镜头。
镜头基本上是不可变数据的获取器和设置器。镜头聚焦在较大结构的一小部分上。给定一个镜头,您可以使用两种方法进行操作-您可以查看较大结构值的一小部分,或者可以将较大结构值的一小部分设置为新值。例如,假设您有一个镜头聚焦在列表中的第三项上:
thirdItemLens :: Lens [a] a
该类型表示较大的结构是事物的列表,而较小的子部分是这些事物之一。使用此镜头,您可以查看和设置列表中的第三项:
> view thirdItemLens [1, 2, 3, 4, 5]
3
> set thirdItemLens 100 [1, 2, 3, 4, 5]
[1, 2, 100, 4, 5]
镜头之所以有用,是因为它们是代表getter和setter的值,您可以像处理其他值一样对它们进行抽象。您可以创建返回镜片的listItemLens
功能,例如,一个带数字n
并返回查看n
列表中第一个项目的镜片的功能。另外,镜片可以组成:
> firstLens = listItemLens 0
> thirdLens = listItemLens 2
> firstOfThirdLens = lensCompose firstLens thirdLens
> view firstOfThirdLens [[1, 2], [3, 4], [5, 6], [7, 8]]
5
> set firstOfThirdLens 100 [[1, 2], [3, 4], [5, 6], [7, 8]]
[[1, 2], [3, 4], [100, 6], [7, 8]]
每个镜头都封装了用于遍历数据结构一级的行为。通过将它们组合在一起,可以消除用于遍历复杂结构的多个级别的样板。例如,假设您有一个在场景scenePolygonLens i
中查看第i
th个多边形,一个在多边形中polygonPointLens n
查看nth
Point的对象,则可以使一个镜头构造函数专注于整个场景中您关注的特定点,如下所示:
scenePointLens i n = lensCompose (polygonPointLens n) (scenePolygonLens i)
现在,假设用户单击多边形14的点3并将其向右移动10个像素。您可以像这样更新场景:
lens = scenePointLens 14 3
point = view lens currentScene
newPoint = movePoint 10 0 point
newScene = set lens newPoint currentScene
这很好地包含了用于遍历和更新内部场景的所有样板lens
,您只需关心要更改点的位置即可。您可以使用lensTransform
接受镜头,目标的功能以及通过镜头更新目标的视图的功能来进一步抽象此功能:
lensTransform lens transformFunc target =
current = view lens target
new = transformFunc current
set lens new target
这需要一个函数,并将其转换为复杂数据结构上的“更新器”,仅将该函数应用于视图并使用它来构建新视图。因此,回到将第14个多边形的第3个点移到右边的10个像素的情况下,可以这样表示lensTransform
:
lens = scenePointLens 14 3
moveRightTen point = movePoint 10 0 point
newScene = lensTransform lens moveRightTen currentScene
这就是您更新整个场景所需的全部。这是一个非常有力的想法,当您具有一些构造镜头的功能时,可以很好地工作,从而查看您关心的数据片段。
但是,即使在函数式编程社区中,目前所有这些东西也都是多余的。很难找到有关使用镜头的良好库支持,甚至更难解释它们如何工作以及对您的同事有什么好处。用这种方法加一点盐。