【问题标题】:What's the neatest way to define circular lists with Scala?用 Scala 定义循环列表的最佳方法是什么?
【发布时间】:2014-02-05 13:53:42
【问题描述】:

这是我的尝试:

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 应该延迟计算?

【问题讨论】:

    标签: scala circular-list


    【解决方案1】:

    你可以像这样使用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)
    

    【讨论】:

      【解决方案2】:

      你需要让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

      【讨论】:

      • +1 用于代理示例;这很性感。这种方法有什么缺点吗?最重要的是,A 类的平等表现如何?
      • 可能最突出的一个是它需要一个额外的实例来让你的字段变得懒惰。第一个(手动)解决方案使用标准惰性 val,每个值只需要一个位(而不是 aful 对象)。
      【解决方案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 类。

      【讨论】:

        【解决方案4】:

        您可以使用按名称参数。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-20
          • 1970-01-01
          • 2011-02-25
          • 1970-01-01
          • 1970-01-01
          • 2017-02-20
          • 1970-01-01
          相关资源
          最近更新 更多