Scala Referenz in Klasse A auf Klasse B und umgekehrt

abertram

Mitglied
Hallo Leute,

ich habe zwei Klassen A und B. Beim Erzeugen einer Instanz der Klasse A möchte ich eine Referenz auf eine Instanz der Klasse B speichern. Das Problem ist, dass die B-Instanz erst nach der A-Instanz erzeugt wird.

Java:
class A(val b: B) {}

class B(val a: A) {}

val a = new A(b)
val b = new B(a)

Ich hoffe, der Code macht es deutlich. Ich möchte es vermeiden, mit vars zu arbeiten.
Gibt es vielleicht sowas wie eine verzögertes Setzen eines Wertes, sodass ich die Referenz auf B in A nachträglich setzen kann?

Ich hoffe, ich habe mich verständlich ausgedrückt.

Gruß,
Alex
 

Antoras

Top Contributor
Du musst nicht immer unveränderliche Werte benutzen - nicht immer ist es angebracht. Bei deinem Problem wäre es aber interessant zu erfahren wieso die beiden Klassen Abhängigkeiten zueinander besitzen. Vielleicht kann man ja etwas am Aufbau ändern.
 

abertram

Mitglied
Wenn es nicht anders geht, dann muss ich wohl mit einem Setter arbeiten.

Ich habe folgendes vor:

Ich will OpenStreetMap-Daten parsen, die aus Knoten und Wegen bestehen. Dabei sind in den Wegen Referenzen auf Knoten gespeichert, aus denen der Weg besteht. Jetzt möchte ich gerne zuerst die Knoten parsen und dabei Referenzen auf Wege speichern, zu denen dieser Knoten gehört. Beim Parsen der Wege speichere ich Referenzen auf Knoten, die zu diesem Weg gehören.

Gruß,
Alex
 

Antoras

Top Contributor
Warum nimmst du nicht einfach eine Map und speichert die Knoten und Referenzen dort? Falls du die Daten in der Map später lieber noch als eigenen Graph haben möchtest, kannst du diesen nach dem Parsen ja immer noch anlegen.
 

Antoras

Top Contributor
Du hast so etwas wie Map[Ref, List[N]]. Ref ist dabei der Anfang des Weges, die Liste repräsentiert die Knoten, die auf dem Weg liegen.
Daraus kannst du dann ein
Code:
class Node(v: Value, begin: Ref)
machen. Ich weiß nicht ob ich überhaupt verstanden habe was du erreichen möchtest, wenn das nicht passt deine Ziele einfach nochmal beschreiben.
 

abertram

Mitglied
Du hast so etwas wie Map[Ref, List[N]]. Ref ist dabei der Anfang des Weges, die Liste repräsentiert die Knoten, die auf dem Weg liegen.

Ok, dann habe ich dich richtig verstanden.

Ich weiß nicht ob ich überhaupt verstanden habe was du erreichen möchtest, wenn das nicht passt deine Ziele einfach nochmal beschreiben.

Ich parse Knoten, die aus einer Id, geographischen Koordinaten und weiteren Daten bestehen,

[XML]<node id="25496583" lat="51.5173639" lon="-0.140043" version="1" changeset="203496" user="80n" uid="1238" visible="true" timestamp="2007-01-28T11:40:26Z">
<tag k="highway" v="traffic_signals"/>
</node>[/XML]

und Wege, die aus einer Id, den Knoten-Referenzen und weiteren Daten bestehen

[XML]<way id="5090250" visible="true" timestamp="2009-01-19T19:07:25Z" version="8" changeset="816806" user="Blumpsy" uid="64226">
<nd ref="822403"/>
<nd ref="21533912"/>
<nd ref="821601"/>
<nd ref="21533910"/>
<nd ref="135791608"/>
<nd ref="333725784"/>
<nd ref="333725781"/>
<nd ref="333725774"/>
<nd ref="333725776"/>
<nd ref="823771"/>
<tag k="highway" v="unclassified"/>
<tag k="name" v="Clipstone Street"/>
<tag k="oneway" v="yes"/>
</way>[/XML]

Für beide Strukturen habe ich eigene Klassen vorgesehen: Node und Way. Wenn ich auf eine Knoteninstanz zugreife, möchte ich gerne wissen, in welchen Wegen dieser Knoten auftritt. Umgekehrt möchte ich wissen, aus welchen Knoten ein Weg besteht, wenn ich auf eine Weginstanz zugreife.

Ich parse zuerst die Knoten und erzeuge pro Knoten eine Instanz. Zu diesem Zeitpunkt habe ich ja noch keine Weginstanzen. Wenn ich nun die Wege parse, weiß ich, welche Knoten zu welchem Weg gehören. Das ist ja bereits in der Weg-Struktur gegeben.

Ist diese Erklärung besser?

Gruß,
Alex
 

Antoras

Top Contributor
Also, mir ist jetzt klar was du machen möchtest, kann aber natürlich nicht beurteilen welche Lösung für deinen Anwendungsfall die Beste ist, da ich Details wie verfügbare Leistung, Anzahl an Knoten/Wegen etc. nicht kenne.

Wenn genug Speicherplatz vorhanden ist, kannst du die Knoten und Referenzen wie bisher von dir geplant erstellen. Danach erstellst du noch eine Map[Node, List[Ref]], in der zu jedem Knoten noch die Referenzen stehen, die auf den Knoten zeigen.
Eine andere Möglichkeit wäre die Nodes nach dem Parsen der Referenzen einfach neu zu erstellen. Oder du erstellst am Anfang nur einen n-Tupel und erst wenn die Informationen der Referenzen verfügbar sind kümmerst du dich um die Erstellung der Nodes.

So könntest du vorgehen, wenn die Lösung komplett immutable sein soll. Ob das sinnvoll ist oder ob es besser ist doch mutable zu arbeiten musst du entscheiden.
 

abertram

Mitglied
Ich habe mir noch mal Gedanken gemacht und beschlossen, die Klasse Node nicht zu erweitern sondern die Struktur separat abzuspeichern.

Gestern Abend habe ich mit dem Versuch verbracht, diese Struktur zu erstellen und es leider nicht hinbekommen... :(

Ich möchte euch also um weitere Hilfe bitten.

Meine Klassen sehen so aus (auf das Wesentliche beschränkt):

Java:
class Node(val id: Int)

class Way(val id: Int, val nodes: Vector[Node])

Node und Way haben jeweils eine ID und in Way ist ein Vektor von Knoten gespeichert, die auf diesem Weg liegen.

Jetzt möchte ich gerne einen Knoten auf eine Menge von Wegen abbilden, auf denen der Knoten zu finden ist. Ich denke da an folgende Struktur:

Java:
IntMap[HashSet[Way]]

Wobei der Schlüssel die Knoten-ID ist und der Wert die Menge der Wege.

Ich habe folgenden Ansatz versucht:

Über die Knoten iterieren und die Wege filtern:

Java:
IntMap[HashSet[Way]](nodes.values map (node => {
  (node id, (ways.values filter (way => way.nodes contains node)).toSet)
}): _*)

Das war nicht kompilierfähig, weil die Typen nicht übereinstimmen. Aber warum? Und wie kriege ich es hin?

Gruß,
Alex
 

Antoras

Top Contributor
Code:
IntMap[HashSet[Way]]
Was soll das darstellen?

Schau dir mal das hier an:
Code:
import util.Random._
case class Node(id: Int)
case class Way(id: Int, nodes: Seq[Node])
val nodes = 1 to 100 map Node
val ways = 1 to 10 map { Way(_, Seq.fill(10)(nodes(nextInt(100)))) }
def find(n: Node) = ways collect { case Way(i, nodes) if nodes contains n => i }
val map = (nodes map { n => n -> find(n) }).toMap
Ist nicht sonderlich effizient, vermittelt dir aber vielleicht wie ich mir das vorgestellt habe.
 

abertram

Mitglied
Code:
IntMap[HashSet[Way]]
Was soll das darstellen?

Das ist nichts anderes als
Code:
Map[Int, HashSet[Way]]
also die Abbildung eines Knotens auf eine Menge von Wegen.

Schau dir mal das hier an:
...

Mit deinem Code-Beispiel hast du mich auf die entscheidende Idee gebracht, danke!

Ich habe das Problem jetzt so gelöst:

Java:
nodes map (node => {
  (node id, (ways filter (way => way.nodes contains node)) toSet)
}) toMap

Ist auch nicht besonders effizient. Aber da diese Struktur nur ein Mal am Anfang aufgebaut wird, ist das kein großes Problem.

Gruß,
Alex
 

Neue Themen


Oben