Я мог бы хранить индексы полигона в текущей сцене, индекс перетаскиваемой точки в полигоне и заменять его каждый раз. Но этот подход не масштабируется - когда уровень композиции становится равным 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]
Линзы полезны потому, что они представляют собой значения, представляющие методы получения и установки, и вы можете абстрагироваться над ними так же, как и другие значения. Вы можете создавать функции, которые возвращают линзы, например, 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
который просматривает 3- i
й полигон в сцене, и объект, polygonPointLens n
который просматривает nth
точку в полигоне, вы можете создать конструктор линз для фокусировки только на конкретной точке, которая вас интересует во всей сцене, например:
scenePointLens i n = lensCompose (polygonPointLens n) (scenePolygonLens i)
Теперь предположим, что пользователь щелкает точку 3 многоугольника 14 и перемещает ее на 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
Это берет функцию и превращает ее в «средство обновления» для сложной структуры данных, применяя функцию только к представлению и используя ее для создания нового представления. Итак, возвращаясь к сценарию перемещения 3-й точки 14-го многоугольника вправо на 10 пикселей, это можно выразить lensTransform
примерно так:
lens = scenePointLens 14 3
moveRightTen point = movePoint 10 0 point
newScene = lensTransform lens moveRightTen currentScene
И это все, что вам нужно, чтобы обновить всю сцену. Это очень мощная идея, и она очень хорошо работает, когда у вас есть несколько полезных функций для создания линз, которые просматривают фрагменты ваших данных, которые вам небезразличны.
Однако в настоящее время все это довольно просто, даже в сообществе функционального программирования. Трудно найти хорошую библиотечную поддержку для работы с линзами, и еще сложнее объяснить, как они работают и каковы преимущества для ваших коллег. Возьмите этот подход с зерном соли.