为什么"懒惰"是关键字而不是标准库类型?

 mobiledu2502872877 发布于 2023-02-08 10:41

Scala在其标准库中保留了许多非常有用的构造,如Option和Try.

当缺少上述类型的C#等语言选择将其作为库功能实现时,为什么懒惰通过拥有自己的关键字给予特殊处理?

2 个回答
  • 确实可以定义一个惰性值,例如:

    object Lazy {  
      def apply[A](init: => A): Lazy[A] = new Lazy[A] {
        private var value = null.asInstanceOf[A]
        @volatile private var initialized = false
    
        override def toString = 
          if (initialized) value.toString else "<lazy>@" + hashCode.toHexString
    
        def apply(): A = {
          if (!initialized) this.synchronized {
            if (!initialized) {
              value = init
              initialized = true
            }
          }
          value
        }
      }
    
      implicit def unwrap[A](l: Lazy[A]): A = l()
    }     
    
    trait Lazy[+A] { def apply(): A }
    

    用法:

    val x = Lazy {
      println("aqui")
      42
    }
    
    def test(i: Int) = i * i
    
    test(x)
    

    另一方面,具有lazy作为语言提供的修饰符具有允许其参与统一访问原则的优点.我试图查找一个博客条目,但没有任何超越getter和setter.这个原则实际上更为基础.对于值,以下是统一的:val,lazy val,def,var,object:

    trait Foo[A] {
      def bar: A
    }
    
    class FooVal[A](val bar: A) extends Foo[A]
    
    class FooLazyVal[A](init: => A) extends Foo[A] {
      lazy val bar: A = init
    }
    
    class FooVar[A](var bar: A) extends Foo[A]
    
    class FooProxy[A](peer: Foo[A]) extends Foo[A] {
      def bar: A = peer.bar
    }
    
    trait Bar {
      def baz: Int
    }
    
    class FooObject extends Foo[Bar] {
      object bar extends Bar {
        val baz = 42
      }
    }
    

    在Scala 2.6中引入了惰性值.有一个Lambda终极评论表明推理可能与形式化循环引用的可能性有关:

    循环依赖关系需要与惰性值绑定.延迟值还可用于强制按依赖顺序进行组件初始化.遗憾的是,组件关闭顺序必须手动编码

    我不知道为什么编译器无法自动处理循环引用; 也许有复杂性或表现性的原因.一个博客帖子是Iulian Dragos的证实一些假设.

    2023-02-08 10:42 回答
  • 当前的延迟实现使用int位掩码来跟踪字段是否已初始化,并且没有其他内存开销.该字段在多个惰性val之间共享(每个字段最多32个惰性值).实现具有与库特征类似的存储器效率的特征是不可能的.

    作为库的懒惰可能看起来大致如下:

    class LazyVal[T](f: =>T) {
      @volatile private var initialized = false
    
      /*
       this does not need to be volatile since there will always be an access to the
       volatile field initialized before this is read.
      */
      private var value:T = _ 
      def apply() = {
        if(!initialized) {
          synchronized {
            if(!initialized) {
              value = f
              initialized = true
            }
          }
        }
        value
      }
    }
    

    这的开销将是生成值的闭包f的对象,以及LazyVal本身的另一个对象.因此,对于经常使用的功能来说,这将是很重要的.

    在CLR上你有值类型,所以如果在C#中将LazyVal实现为struct,那么开销就不那么糟了

    但是,现在宏可用,将惰性转换为库特征或至少允许自定义延迟初始化可能是个好主意.许多lazy val的使用情况不需要线程同步,因此每次使用lazy val时都要浪费@ volatile/synchronized开销.

    2023-02-08 10:43 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有