Подтвердить что ты не робот

Как инициализировать и "модифицировать" циклическую постоянную структуру данных в Scala?

Я искал и нашел некоторую информацию по этой теме, но ответы либо запутывают, либо не применимы.

У меня есть что-то вроде этого:

class Thing (val name:String, val refs:IndexedSeq[Ref])
class Ref (val name:String, val thing:Thing)

Теперь я хочу сказать, загрузите файл, проанализируйте его и заполните эту структуру данных. Это неизменный и циклический, как это можно сделать?

Кроме того, скажем, я действительно заполняю эту структуру данных, теперь я хочу ее изменить, например, изменить rootThing.refs(3).name, как это можно сделать?


Спасибо за идеи, опубликованные здесь. На данный момент я думаю, что если вам действительно нужны постоянные структуры данных для чего-то вроде этого, подумайте нестандартно и подумайте над тем, какие вопросы должен задать клиентский код. Поэтому вместо того, чтобы думать об объектах и ​​полях, подумайте о запросах, индексах и т.д. Для начала я думаю с точки зрения: Существует ли двунаправленная многофазная постоянная структура данных?

4b9b3361

Ответ 1

Для одной циклической ссылки вы можете использовать ленивый:

lazy val t: Thing = new Thing("thing", Vector(new Ref("ref", t)))

Однако, очевидно, это осложняется соединением "многие-ко-многим".

Я не знаю, существует ли сугубо функциональная структура циклического графа. С ациклическими графами это было бы легко, поскольку вы могли бы топологически отсортировать его, а затем инициализировать его шаг за шагом.

Может быть, использование косвенности является опцией для вас, скажем, ссылаться на объекты через идентификатор вместо фактической ссылки на объект scala?

case class ThingByID(id: Int, name: String, refs: IndexedSeq[RefByID])
case class RefByID(name: String, thingID: Int)

Затем вы можете после загрузки вашего файла собрать вещи по их идентификатору в неизменяемую карту (например, collection.immutable.IntMap) и посмотреть их, когда пришли из ссылки.

ИЗМЕНИТЬ

Мили правильно относятся к первому случаю lazy val t. Действительно, вам нужны параметры по имени, как в его ответе.

class Thing(val name: String, val refs: IndexedSeq[Ref])
class Ref(val name: String, _thing: => Thing) { def thing = _thing }

val t: Thing = new Thing("thing", Vector(new Ref("ref", t)))

Ответ 2

Вы можете инициализировать структуру циклических данных этой формы, если вы готовы изменить ее, чтобы ввести степень лени,

scala> class Thing (val name:String, refs0: => IndexedSeq[Ref]) { lazy val refs = refs0 } ; class Ref (val name:String, thing0: => Thing) { lazy val thing = thing0 }
defined class Thing
defined class Ref

scala> val names = Vector("foo", "bar", "baz")                                                                                                                       
names: scala.collection.immutable.Vector[java.lang.String] = Vector(foo, bar, baz)

scala> val rootThing : Thing = new Thing("root", names.map { new Ref(_, rootThing) })
rootThing: Thing = [email protected]

scala> rootThing.refs(1).name
res0: String = bar

Однако вы не можете сделать это постоянным: будучи циклическим, любые изменения видны через каждый элемент структуры, поэтому нет возможности для обмена версиями.

Ответ 3

Существует альтернативный подход, который требует переосмысления того, как представляются ассоциации объектов: вместо хранения ассоциаций между объектами внутри самих объектов (как это обычно делается в OO-коде) добавьте их впоследствии на отдельный слой в качестве Maps.

Поскольку все узлы в графе объектов существуют по временным ассоциациям, неизменяемые ассоциации с двунаправленными могут быть легко созданы с помощью Maps.

scala> class Thing (val name:String)
defined class Thing

scala> class Ref (val name:String)
defined class Ref

scala> new Thing("Thing1")
res0: Thing = [email protected]

scala> new Ref("Ref1")
res1: Ref = [email protected]       

scala> val thing2Ref = Map(res0 -> res1)
thing2Ref: scala.collection.immutable.Map[Thing,Ref] = Map([email protected] -> Ref
@7656acfa)

scala> val ref2Thing = Map(res1 -> res0)
ref2Thing: scala.collection.immutable.Map[Ref,Thing] = Map([email protected] -> Thing
@5c2bae98)

Если вы думаете об этом, этот подход похож на то, как работают реляционные базы данных. Многозначные связи между таблицами не хранятся в самих строках, а в отдельных индексах. Даже если индексы ассоциации отсутствуют и поэтому запрос разрешен с использованием сканирования таблицы, он использует индексы первичного ключа для поиска всех строк-кандидатов для результата.

Преимущество этого стиля заключается в том, что ассоциации могут быть добавлены или изменены без ущерба для самих объектов, и различные ассоциации могут использоваться для разных аудиторий/прецедентов. Недостатком является то, что карта ассоциации не доступна непосредственно в случаях, когда начинается объединение; он должен быть передан и предоставлен отдельно.

Ответ 4

Непрерывные структуры данных могут быть полностью инициализированы их конструктором, или вы можете согласиться с необходимостью копировать структуру при изменении ее свойств. Поэтому, чтобы ответить на первую часть вопроса, вы загружаете данные в неизменяемую структуру данных, определяя конструктор, который принимает всю информацию в вашей базе данных, или убедитесь, что вы знаете о циклических подграфах:

Циклические структуры данных не обязательно полностью цикличны, я думаю. Если вы представляете себе сеть указателей, что имеет место один экземпляр/состояние, вы можете иметь подграф, содержащий родительский элемент и дочерний элемент, которые указывают друг на друга, но не имеют других циклических структур. В этом случае копирование экземпляра 1 для ленивого создания экземпляра 2 с другим родителем node (например) потребовало бы копирования родительского и дочернего узлов, поскольку они образуют циклический подграф. Но ссылки, проведенные внутри дочернего элемента, отличного от родителя, могут оставаться ссылками на те же неизменяемые структуры, что и первый экземпляр.

Например, в моем классе House есть ссылка на дверь, окно и крышу. Дверь имеет цвет и ссылку toHouse на дом, окно имеет размер и крыша имеет высоту тона. Поэтому я создаю bobsHouse с зеленой дверью, большим окном и плоской крышей. Фактически, поскольку все они неизменяемы, теоретически существует только одно большое окно - все дома с большими окнами имеют одинаковое Окно. Второй экземпляр, janesHouse, похож на bobsHouse, но с остроконечной крышей. Поэтому, если я скажу janesHouse = bobsHouse.withRoof(gabled), тогда я должен получить новый экземпляр House с новой (также зеленой) дверью и новой (gabled) крышей, но с тем же окном.

Итак, если janesHouse оценивается лениво, ему нужно создать только новый дом, если будут указаны ссылки на дверь или крышу. Если запрашивается janesHouse.Window, не нужно создавать новый дом вообще - требуется только bobsHouse.

tl; dr: вы можете иметь постоянные (ленивые) циклические структуры данных, но только если вы можете найти в нем нециклические подграфы, т.е. это не цепочка.