2014-02-05 52 views
5

這裏是我的嘗試:用Scala定義循環列表最簡單的方法是什麼?

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 a應該被懶惰地計算出來?

回答

2

你需要使A.a本身懶惰。

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

它具有:

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做同樣的: 您可以通過用來初始化一個懶惰的字段名稱參數把它變成一個做的優點是,你的代碼是除了改變了a: Aa: Lazy[A]幾乎不變:

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

注意訪問包裹在Lazy的實際值,既可以使用applyvalue(如在x.a()x.a.value

+0

1爲代理示例;這很性感。這種方法的缺點?最重要的是,平等對於A類如何表現? –

+0

可能最突出的一點是它需要一個額外的實例來讓你的領域變得懶惰。 第一個(手動)解決方案使用標準的lazy val,並且這些只需要每個值一位(而不是有用對象)。 –

7

您可以使用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) 
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) 

請注意,這不符合情況類工作。

2

您可以使用by-name參數。

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) 
} 
相關問題