gpt4 book ai didi

scala - Scala trait mixin 中的方法调用顺序

转载 作者:行者123 更新时间:2023-12-04 15:17:50 25 4
gpt4 key购买 nike

我有一个程序结构如下:

abstract class IntQueue {
def get(): Int
def put(x: Int)
}
trait Doubling extends IntQueue{
abstract override def put(x: Int) {
println("In Doubling's put")
super.put(2*x)
}
}
trait Incrementing extends IntQueue {
abstract override def put(x: Int) {
println("In Incrementing's put")
super.put(x + 1)
}
}
class BasicIntQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x: Int) {
println("In BasicIntQueue's put")
buf += x
}
}
当我做:
val incrThendoublingQueue = new BasicIntQueue with Doubling with 
Incrementing
incrThendoublingQueue.put(10)
println(incrThendoublingQueue.get())
输出是:

In Incrementing's put

In Doubling's put

In BasicIntQueue's put

22


我对在这里订购有点困惑。我对这种情况的线性化顺序的理解是:

BasicIntQueue -> Incrementing -> Doubling -> IntQueue -> AnyRef -> Any


所以当我调用 put 时,不应该先调用 BasicIntQueue 的版本吗?

最佳答案

不。这种情况下的线性化是

{<anonymous-local>, Incrementing, Doubling, BasicIntQueue, IntQueue, AnyRef, Any}

你可以:
  • 只需阅读规范并说服自己一定是这样
  • 从规范中实现算法的玩具版本,看看它为各种类定义输出了什么(这有点启发,但主要是为了好玩)


  • 阅读规范

    section 5.1.2 of the Spec
    准确地告诉您如何计算线性化。您似乎忘记了反转索引 1 ... nL(c_n) + ... + L(c_1) .

    如果您应用正确的公式,您将获得涉及的特征和基类的以下线性化:
         IntQueue : {IntQueue, AnyRef, Any}
    Doubling : {Doubling, IntQueue, AnyRef, Any}
    Incrementing : {Incrementing, IntQueue, AnyRef, Any}
    BasicIntQueue : {BasicIntQueue, IntQueue, AnyRef, Any}

    如果您最终组合这些线性化以计算实例化为 incrThendoublingQueue 的匿名本地类的线性化。 :
    <anonymous-local-class>, L(Incrementing) + L(Doubling) + L(BasicInt)

    您获得了上面已经显示的线性化。因此,应按以下顺序调用这些方法:
  • 递增
  • 加倍
  • 基本

  • 这与实际输出一致。

    重新实现线性化算法的乐趣

    这实际上是规范的无依赖片段之一,您可以轻松地从头开始实现。这
    可以复制带有替换的连接的定义
    规范原样,它几乎是可运行的代码(除了带箭头的有趣加号有点难以输入,我希望它作为列表中的中缀运算符):
    implicit class ConcatenationWithReplacementOps[A](list: List[A]) {
    def +^->(other: List[A]): List[A] = list match {
    case Nil => other
    case h :: t =>
    if (other contains h) (t +^-> other)
    else h :: (t +^-> other)
    }
    }

    对类声明建模 C extends C1 with ... with Cn也是
    真的很简单:
    case class ClassDecl(c: String, extendsTemplate: List[ClassDecl]) {
    def linearization: List[String] = c :: (
    extendsTemplate
    .reverse
    .map(_.linearization)
    .foldLeft(List.empty[String])(_ +^-> _)
    )
    }

    线性化公式在这里作为一种方法实现。注意 reverse .

    规范中给出的示例:
    val any = ClassDecl("Any", Nil)
    val anyRef = ClassDecl("AnyRef", List(any))
    val absIterator = ClassDecl("AbsIterator", List(anyRef))
    val richIterator = ClassDecl("RichIterator", List(absIterator))
    val stringIterator = ClassDecl("StringIterator", List(absIterator))
    val iter = ClassDecl("Iter", List(stringIterator, richIterator))

    println(iter.linearization.mkString("{", ", ", "}"))

    产生与规范完全相同的输出:
    {Iter, RichIterator, StringIterator, AbsIterator, AnyRef, Any}

    现在,这是您示例的模型:
    val intQueue = ClassDecl("IntQueue", List(anyRef))
    val doubling = ClassDecl("Doubling", List(intQueue))
    val incrementing = ClassDecl("Incrementing", List(intQueue))
    val basicQueue = ClassDecl("BasicIntQueue", List(intQueue))

    val incrThendoublingQueue = ClassDecl(
    "<anonymous-local>",
    List(basicQueue, doubling, incrementing)
    )

    println(incrThendoublingQueue.linearization.mkString("{", ", ", "}"))

    它产生我上面已经显示的线性化顺序:
    {<anonymous-local>, Incrementing, Doubling, BasicIntQueue, IntQueue, AnyRef, Any}

    一切似乎都按预期工作,没有理由写信给 Scala 用户。

    关于scala - Scala trait mixin 中的方法调用顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49831404/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com