Как инициализировать и «модифицировать» циклическую постоянную структуру данных в Scala? - PullRequest
12 голосов
/ 16 апреля 2011

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

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

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

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

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


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

Ответы [ 4 ]

13 голосов
/ 16 апреля 2011

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

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 = Thing@1f7dab1

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

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

5 голосов
/ 06 октября 2011

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

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

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

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

scala> new Thing("Thing1")
res0: Thing = Thing@5c2bae98

scala> new Ref("Ref1")
res1: Ref = Ref@7656acfa       

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

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

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

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

5 голосов
/ 16 апреля 2011

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

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) и найти их, когда пришли из ссылки.

EDIT

Майлз прав насчет первого случая 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)))
3 голосов
/ 17 апреля 2011

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...