问题描述
我的程序结构如下:
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 ->任何
所以当我调用 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}
您可以:
- 只需阅读规范并说服自己一定是这样
- 从规范中实现算法的玩具版本,看看它为各种类定义输出了什么(这有点启发,但主要是为了好玩)
阅读规范
规范第 5.1.2 节准确地告诉您如何计算线性化.您似乎忘记了反转 L(c_n) + ... + L(c_1)
中的索引 1 ... n
.
The section 5.1.2 of the Spectells 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. Thedefinition of concatenation with replacement can be copied fromthe 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 alsoreally 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 中的方法调用顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!