What's the neatest way to define circular lists with Scala?

时光总嘲笑我的痴心妄想 提交于 2020-01-02 03:27:07

问题


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?


回答1:


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)




回答2:


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)



回答3:


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.




回答4:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!