2014-02-05 5 views
5

Hier ist mein Versuch:Was ist der beste Weg, um kreisförmige Listen mit Scala zu definieren?

case class A(val a: A, val b: Int){ 
    override def toString() = b.toString 
} 

lazy val x: A = A(y, 0) 
lazy val y: A = A(z, 1) 
lazy val z: A = A(x, 2) 

Das Problem kommt, wenn etwas mit x zu tun versuchen; Wenn x veranlaßt wird, beginnt es mit einer kreisförmigen Auswertung, die x, y, z durchläuft und endet in einem Stapelüberlauf. Gibt es eine Möglichkeit zu spezifizieren, dass val a träge berechnet werden sollte?

Antwort

2

Sie müssen A.a selbst faul machen. Sie können es tun, indem sie in einen mit Namen Parameter drehen, die einen faulen Feld zu initialisieren verwendet wird:

class A(a0: => A, val b: Int){ 
    lazy val a = a0 
    override def toString() = b.toString 
} 
object A { 
    def apply(a0: => A, b: Int) = new A(a0, b) 
} 

Sie auch das gleiche tun könnte eine Hilfsklasse mit Lazy:

implicit class Lazy[T](getValue: => T) extends Proxy { 
    def apply(): T = value 
    lazy val value = getValue 
    def self = value 
} 

Es hat den Vorteil, dass Sie Code ziemlich unverändert, außer für a: A in a: Lazy[A] Wechsel:

case class A(val a: Lazy[A], val b: Int){ 
override def toString() = b.toString 
} 

Beachten Sie, dass den tatsächlichen Wert in Lazy gewickelt zuzugreifen, entweder Sie apply oder value (wie in x.a() oder x.a.value)

+0

+1 für die Proxy Beispiel verwenden können; Das ist ziemlich sexy. Irgendwelche Nachteile dieser Methode? Wie verhält sich die Gleichheit für die Klasse A? –

+0

Wahrscheinlich der prominenteste ist, dass es eine zusätzliche Instanz benötigt, nur um Ihr Feld faul zu machen. Die erste (manuelle) Lösung verwendet ein Standard-Lazy-Wert, und diese benötigen nur ein Bit (im Gegensatz zu einem Objekt) pro Wert. –

7

Sie Stream wie diese verwenden:

lazy val stream: Stream[Int] = 0 #:: 1 #:: 2 #:: stream 

stream.take(10).toList 
// List(0, 1, 2, 0, 1, 2, 0, 1, 2, 0) 

Im Allgemeinen sollten Sie call-by-name Parameter verwenden:

class A(_a: => A, val b: Int) { 
    lazy val a = _a 
    override def toString() = s"A($b)" 
} 

Verbrauch:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

lazy val x: A = new A(y, 0) 
lazy val y: A = new A(z, 1) 
lazy val z: A = new A(x, 2) 

// Exiting paste mode, now interpreting. 

x: A = <lazy> 
y: A = <lazy> 
z: A = <lazy> 

scala> z.a.a.a.a.a 
res0: A = A(1) 
3

Sie können einen faulen Kreis Liste definieren mit der Stream Datentyp:

lazy val circular: Stream[Int] = 1 #:: 2 #:: 3 #:: circular 

Sie können auf eigene Faust mit by-name Parameter den gleichen Trick:

class A(head: Int, tail: => A) 
lazy val x = new A(0, y) 
lazy val y = new A(1, z) 
lazy val z = new A(2, x) 

Beachten Sie, dass dies nicht mit Fall Klassen arbeiten.

2

Sie könnten einen Parameternamen verwenden.

class A(__a: => A, val b: Int) { 
    def a = __a 
    override def toString() = b.toString 
} 
object A { 
    def apply(a: => A, b: Int) = new A(a, b) 
} 
Verwandte Themen