Here is my attempt:
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)
The problem comes when trying to do anything with x; causing x to be evaluated starts off a circular evaluation going through x, y, z and ends in a stack overflow. Is there a way of specifying that val a should be computed lazily?
You need to make A.a
itself lazy.
You can do it by turning it into a by name parameter that is used to initialize a lazy field:
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 )
}
You could also do the same using a helper class Lazy
:
implicit class Lazy[T]( getValue: => T ) extends Proxy {
def apply(): T = value
lazy val value = getValue
def self = value
}
It has the advantage that you code is pretty much unchanged except for changing a: A
into a: Lazy[A]
:
case class A(val a: Lazy[A], val b: Int){
override def toString() = b.toString
}
Note that to access the actual value wrapped in Lazy
, you can either use apply
or value
(as in x.a()
or x.a.value
)
You could use Stream
like this:
lazy val stream: Stream[Int] = 0 #:: 1 #:: 2 #:: stream
stream.take(10).toList
// List(0, 1, 2, 0, 1, 2, 0, 1, 2, 0)
In general you should use call-by-name
parameters:
class A(_a: => A, val b: Int) {
lazy val a = _a
override def toString() = s"A($b)"
}
Usage:
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)
You can define a lazy circular list using the Stream
data type:
lazy val circular: Stream[Int] = 1 #:: 2 #:: 3 #:: circular
You can do the same trick on your own with by-name parameters:
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)
Note that this does not work with case classes.
You could use a by-name parameter.
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)
}
来源:https://stackoverflow.com/questions/21579272/whats-the-neatest-way-to-define-circular-lists-with-scala