使用Scala定义循环列表的最佳方法是什么?

时间:2014-02-05 13:53:42

标签: scala circular-list

这是我的尝试:

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)

尝试用x做任何事情时出现问题;导致x被评估的开始是通过x,y,z的循环评估,并以堆栈溢出结束。有没有办法指定应该懒惰地计算val?

4 个答案:

答案 0 :(得分:8)

您可以像这样使用Stream

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

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

通常,您应该使用call-by-name参数:

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

用法:

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)

答案 1 :(得分:3)

您可以使用Stream数据类型定义惰性循环列表:

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

您可以使用名称参数自行执行相同的操作:

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)

请注意,这不适用于 case 类。

答案 2 :(得分:3)

你需要让A.a本身懒惰。 您可以通过将其转换为用于初始化惰性字段的名称参数来执行此操作:

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 )
}

您也可以使用帮助程序类Lazy执行相同的操作:

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

除了将a: A更改为a: Lazy[A]之外,您的代码几乎没有变化:

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

请注意,要访问Lazy中包含的实际值,您可以使用applyvalue(如x.a()x.a.value

答案 3 :(得分:2)

您可以使用按名称参数。

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)
}