Scala trait mixin 中的方法调用顺序 [英] Order of method call in Scala trait mixin

查看:57
本文介绍了Scala trait mixin 中的方法调用顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的程序结构如下:

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

输出为:

增量放置

加倍放球

在 BasicIntQueue 的 put 中

In BasicIntQueue's put

22

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

I am a bit confused about ordering here. My understanding of linearization order for this scenario is:

BasicIntQueue ->递增 ->加倍 ->内部队列 ->AnyRef ->任何

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

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

So when I call put, shouldn't BasicIntQueue's version be called first?

推荐答案

没有.这种情况下的线性化是

No. The linearization in this case is

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

您可以:

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

<小时>

阅读规范

规范第 5.1.2 节准确地告诉您如何计算线性化.您似乎忘记了反转 L(c_n) + ... + L(c_1) 中的索引 1 ... n.

The section 5.1.2 of the Spec tells you precisely how the linearization is computed. You seem to have forgotten to reverse the indices 1 ... n in L(c_n) + ... + L(c_1).

如果你应用正确的公式,你会得到以下涉及特征和基类的线性化:

If you apply the correct formula, you get the following linearizations for the involved traits and base classes:

     IntQueue : {IntQueue, AnyRef, Any}
     Doubling : {Doubling, IntQueue, AnyRef, Any}
 Incrementing : {Incrementing, IntQueue, AnyRef, Any}
BasicIntQueue : {BasicIntQueue, IntQueue, AnyRef, Any}

如果你最终组合这些线性化来计算实例化为 incrThendoublingQueue 的匿名本地类的线性化:

If you then finally combine these linearizations to compute the linearization of the anonymous local class that is instantiated as incrThendoublingQueue:

<anonymous-local-class>, L(Incrementing) + L(Doubling) + L(BasicInt)

您获得了上面已经显示的线性化.因此,应按以下顺序调用这些方法:

you obtain the linearization already shown above. Therefore, the methods should be invoked in this order:

  • 递增
  • 翻倍
  • 基础

与实际输出一致.

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

这实际上是规范的无依赖片段之一,您可以从头开始轻松实现.这可以复制带有替换的连接的定义规范原样,它几乎是可运行的代码(除了带箭头的有趣加号有点难以输入,我希望它作为列表中的中缀运算符):

This is actually one of those dependency-free snippets of the specification that you can implement easily from scratch. The definition of concatenation with replacement can be copied from the spec as-is, it's almost runnable code (except that the funny plus with arrow is somewhat difficult to type, and that I wanted it as an infix operator on lists):

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 也是真的很简单:

Modeling a class declaration C extends C1 with ... with Cn is also really simple:

case class ClassDecl(c: String, extendsTemplate: List[ClassDecl]) {
  def linearization: List[String] = c :: (
    extendsTemplate
      .reverse
      .map(_.linearization)
      .foldLeft(List.empty[String])(_ +^-> _)
  )
}

线性化的公式在这里实现为一种方法.注意反向.

The formula for linearization is implemented as a method here. Notice the 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("{", ", ", "}"))

完全按照规范产生输出:

produces the output exactly as in the spec:

{Iter, RichIterator, StringIterator, AbsIterator, AnyRef, Any}

现在,这是您示例的模型:

Now, here is a model of your example:

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("{", ", ", "}"))    

它产生我上面已经展示的线性化顺序:

It produces the linearization order that I've already shown above:

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

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

Everything seems to work as expected, no reason to write to Scala-Users.

这篇关于Scala trait mixin 中的方法调用顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆