Это, кстати, называется прокруткой экрана. Библиотека, которую я использовал для этого, - Простой HTML-парсер (/ g0).
Интуитивный способ рассуждать о линеаризации - это ссылка на порядок построения и визуализацию линейной иерархии.
Вы могли бы так думать. Сначала создается базовый класс; но прежде, чем он сможет построить базовый класс, сначала должны быть построены его суперклассы / черты (это означает, что конструкция начинается в верхней части иерархии). Для каждого класса в иерархии смешанные черты строятся слева направо, потому что черта справа добавляется «позже» и, таким образом, имеет шанс «переопределить» предыдущие черты. Однако, подобно классам, для того, чтобы построить признак, его базовые черты должны быть построены первыми (очевидными); и, вполне разумно, если черта уже построена (в любом месте иерархии), она не восстанавливается снова. Теперь порядок построения является обратной линеаризации. Подумайте о «базовых» чертах / классах как более высоких в линейной иерархии, а черты ниже в иерархии ближе к классу / объекту, который является предметом линеаризации. Линеаризация влияет на то, как «супер» разрешается в признаке: он будет решать ближайший базовый признак (выше в иерархии).
Таким образом:
var d = new A with D with C with B;
Линеаризация A with D with C with B
является
Таким образом, линеаризация: ADBC. Вы можете думать об этом как о линейной иерархии, где A - корень (самый высокий) и построен сначала, а C - лист (самый низкий) и построенный последним. [3] С учетом этих интуитивных правил d.foo
вызывает C.foo
, который возвращает «C», за которым следует super.foo()
, который разрешен на B
(черта слева от B
, т.е. выше / до, в линеаризации), которая возвращает «B», а затем super.foo()
, которая разрешена на D
, которая возвращает «D», а затем super.foo()
, который разрешен на A
, который, наконец, возвращает «A». Итак, у вас есть «CBDA».
В качестве другого примера я подготовил следующее:
class X { print("X") }
class A extends X { print("A") }
trait H { print("H") }
trait S extends H { print("S") }
trait R { print("R") }
trait T extends R with H { print("T") }
class B extends A with T with S { print("B") }
new B // X A R H T S B (the prints follow the construction order)
// Linearization is the reverse of the construction order.
// Note: the rightmost "H" wins (traits are not re-constructed)
// lin(B) = B >> lin(S) >> lin(T) >> lin(A)
// = B >> (S >> H) >> (T >> H >> R) >> (A >> X)
// = B >> S >> T >> H >> R >> A >> X
Combined
, который расширяет черты A with D with C with B
class Combined extends A with D with C with B {
final <superaccessor> <artifact> def super$foo(): String = B$class.foo(Combined.this);
override def foo(): String = C$class.foo(Combined.this);
final <superaccessor> <artifact> def super$foo(): String = D$class.foo(Combined.this);
final <superaccessor> <artifact> def super$foo(): String = Combined.super.foo();
def <init>(): Combined = {
Combined.super.<init>();
D$class./*D$class*/$init$(Combined.this);
B$class./*B$class*/$init$(Combined.this);
C$class./*C$class*/$init$(Combined.this);
()
}
};
Вы можете читать слева направо. Вот небольшой пример. Три черты будут печатать свое имя при инициализации, то есть расширенной:
scala> trait A {println("A")}
scala> trait B {println("B")}
scala> trait C {println("C")}
scala> new A with B with C
A
B
C
res0: A with B with C = $anon$1@5e025e70
scala> new A with C with B
A
C
B
res1: A with C with B = $anon$1@2ed94a8b
Итак, это основной порядок линеаризации. Таким образом, последний будет перезаписывать предыдущий.
Ваша проблема немного сложнее. Поскольку ваши черты уже расширяют другие черты, которые сами переопределяют некоторые значения предыдущих признаков. Но порядок инициализации left to right
или right will override left
.
Вы должны иметь в виду, что сам признак будет инициализирован первым.
Ну, на самом деле, я вижу, что вы просто изменили линеаризацию конструктора, что я считаю довольно простым, поэтому сначала давайте разобраться в линеаризации конструктора
object Linearization3 {
def main(args: Array[String]) {
var x = new X
println()
println(x.foo)
}
}
class A {
print("A")
def foo() = "A"
}
trait B extends A {
print("B")
override def foo() = super.foo() + "B" // Hence I flipped yours to give exact output as constructor
}
trait C extends B {
print("C")
override def foo() = super.foo() + "C"
}
trait D extends A {
print("D")
override def foo() = super.foo() + "D"
}
class X extends A with D with C with B
Какие выходы:
ADBC
ADBC
Итак, чтобы вычислить вывод, я просто беру классы / черты один за другим слева направо, а затем рекурсивно записываю выходы (без дубликатов), вот как:
class X extends A with D with C with B
object Linearization3 {
def main(args: Array[String]) {
var x = new X
println()
println(x.foo)
}
}
class A {
print("A")
def foo() = "A"
}
trait B extends A {
print("B")
override def foo() = "B" + super.foo()
}
trait C extends B {
print("C")
override def foo() = "C" + super.foo()
}
trait D extends A {
print("D")
override def foo() = "D" + super.foo()
}
class X extends A with D with C with B
Выход:
ADBC
CBDA
Надеюсь, это было достаточно просто для новичка Мне нравится
Принятый ответ замечателен, однако, для упрощения, я хотел бы сделать все возможное, чтобы описать его по-другому. Надежда может помочь некоторым людям.
Когда вы сталкиваетесь с проблемой линеаризации, первый шаг - это рисование дерева иерархии классов и признаков. Для этого конкретного примера дерево иерархии будет примерно таким:
Второй шаг состоит в том, чтобы записать все линеаризация признаков и классов, которые мешают целевой задаче. Вам понадобятся все они в одном до последнего шага. Для этого вам нужно написать только путь к корню. Линеаризация признаков такова:
L(A) = A
L(C) = C -> B -> A
L(B) = B -> A
L(D) = D -> A
Третий шаг - написать линеаризацию проблемы. В этой конкретной задаче мы планируем решить линеаризацию
blockquote>
var d = new A with D with C with B;
. Важно отметить, что существует правило, с помощью которого он разрешает метод вызов, сначала используя поиск в начале, сначала в глубину. Другими словами, вы должны начать писать Линеаризацию с самой правой стороны. Это так: L (B) >> L (C) >> L (D) >> L (A)
Четвертый шаг - самый простой шаг. Просто замените каждую линеаризацию со второго шага на третий шаг. После подстановки у вас будет что-то вроде этого:
D -> A -> C -> B -> A -> D -> A -> A
И последнее, но не менее важное: , теперь вы должны удалить все дублированные классы слева направо. Смелые символы должны быть удалены: D -> A -> C -> B -> A -> D -> A -> A
Вы видите, у вас есть результат: C -> B -> D -> A Поэтому ответ CBDA.
Я знаю, что это не индивидуально глубокое концептуальное описание, но может помочь в качестве дополнения к концептуальному описанию, которое я предполагаю.
Паттерны свойств Scala, поэтому вы можете посмотреть их, добавив их по одному:
new A
=> foo = "A"
with D
=> foo = "DA"
with C
, который складывается with B
=> foo = "CBDA"
with B
ничего не делает, потому что B
уже уложен в C
=> foo = "CBDA"
Вот сообщение в блоге о том, как Scala решает проблему наследования алмазов.
В дополнение к другим андерсерам вы можете найти пошаговое объяснение в результате фрагмента ниже
hljs.initHighlightingOnLoad();
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/highlight.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/styles/zenburn.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<table class="table">
<tr>
<th>Expression</th>
<th>type</th>
<th><code>foo()</code> result</th>
</tr>
<tr>
<td><pre><code class="scala"> new A </code></pre>
</td>
<td><pre><code class="scala"> A </code></pre>
</td>
<td><pre><code class="scala">"A"</code></pre>
</td>
</tr>
<tr>
<td><pre><code class="scala"> new A with D </code></pre>
</td>
<td><pre><code class="scala"> D </code></pre>
</td>
<td><pre><code class="scala">"DA"</code></pre>
</td>
</tr>
<tr>
<td><pre><code class="scala"> new A with D with C </code></pre>
</td>
<td><pre><code class="scala"> D with C </code></pre>
</td>
<td><pre><code class="scala">"CBDA"</code></pre>
</td>
</tr>
<tr>
<td><pre><code class="scala"> new A with D with C with B </code></pre>
</td>
<td><pre><code class="scala"> D with C </code></pre>
</td>
<td><pre><code class="scala">"CBDA"</code></pre>
</td>
</tr>
</table>
Процесс, с помощью которого scala разрешает супервызов, называется линеаризацией. В вашем примере вы создаете Object как
var d = new A with D with C with B;
Так, как указано в справочных документах scala Здесь вызывается super will разрешается как
l(A) = A >> l(B) >> l(c) >> l(D)
l(A) = A >> B >> l(A) >> l(C) >> l(D)
l(A) = A >> B >> A >> C >> l(B) >> l(D)
l(A) = A >> B >> A >> C >> B >> l(A) >> l(D)
l(A) = A >> B >> A >> C >> B >> A >> l(D)
l(A) = A >> B >> A >> C >> B >> A >> D >> l(A)
l(A) = A >> B >> A >> C >> B >> A >> D >> A
Теперь Начать с левой стороны и удалить дублируемую конструкцию, в которой право будет выигрывать один
, например удалить A, и мы получим
l(A) = B >> C >> B >> D >> A
remove B, и мы получим
l(A) = C >> B >> D >> A
. Здесь у нас нет дубликата. Теперь начинаем звонить с C
CBDA
super.foo в классе C
вызовет foo в B
и foo в B вызове foo в D
и т. Д.
PS здесь l (A) - линеаризация A