Множественное наследование наследования в scala [duplicate]

Это, кстати, называется прокруткой экрана. Библиотека, которую я использовал для этого, - Простой HTML-парсер (/ g0).

32
задан paisanco 12 December 2015 в 18:08
поделиться

7 ответов

Интуитивный способ рассуждать о линеаризации - это ссылка на порядок построения и визуализацию линейной иерархии.

Вы могли бы так думать. Сначала создается базовый класс; но прежде, чем он сможет построить базовый класс, сначала должны быть построены его суперклассы / черты (это означает, что конструкция начинается в верхней части иерархии). Для каждого класса в иерархии смешанные черты строятся слева направо, потому что черта справа добавляется «позже» и, таким образом, имеет шанс «переопределить» предыдущие черты. Однако, подобно классам, для того, чтобы построить признак, его базовые черты должны быть построены первыми (очевидными); и, вполне разумно, если черта уже построена (в любом месте иерархии), она не восстанавливается снова. Теперь порядок построения является обратной линеаризации. Подумайте о «базовых» чертах / классах как более высоких в линейной иерархии, а черты ниже в иерархии ближе к классу / объекту, который является предметом линеаризации. Линеаризация влияет на то, как «супер» разрешается в признаке: он будет решать ближайший базовый признак (выше в иерархии).

Таким образом:

var d = new A with D with C with B;

Линеаризация A with D with C with B является

  • (верхняя часть иерархии) A (построенная сначала как базовый класс)
  • линеаризация DA (не рассматриваемая как A, предшествует) D (D расширяет A)
  • линеаризация СА (не рассматривается как А происходит раньше) B (B продолжается A) C (C продолжается B)
  • линеаризация BA (не рассматривается как A происходит до) B ( не рассматривается как 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
36
ответ дан metaphori 27 August 2018 в 08:43
поделиться

, как компилятор видит класс 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.

Вы должны иметь в виду, что сам признак будет инициализирован первым.

1
ответ дан Andreas Neumann 27 August 2018 в 08:43
поделиться

Ну, на самом деле, я вижу, что вы просто изменили линеаризацию конструктора, что я считаю довольно простым, поэтому сначала давайте разобраться в линеаризации конструктора

Первый пример

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

Итак, чтобы вычислить вывод, я просто беру классы / черты один за другим слева направо, а затем рекурсивно записываю выходы (без дубликатов), вот как:

  1. Наша подпись класса: class X extends A with D with C with B
  2. Итак, первая - это A, так как у A нет родителей (deadend) просто напечатайте свой конструктор
  3. Теперь D, который расширяет A, поскольку мы уже напечатайте A, затем давайте напечатаем D
  4. Теперь C, который расширяет B, который расширяет A, поэтому мы пропускаем A, потому что он уже напечатан, затем мы печатаем B, а затем печатаем C (это похоже на рекурсивную функцию )
  5. Теперь B, который расширяет A, пропустим A, и мы также пропустим B (ничего не напечатано)
  6. , и вы получили ADBC!

Обратный пример (ваш пример)

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

Надеюсь, это было достаточно просто для новичка Мне нравится

0
ответ дан Ismail Marmoush 27 August 2018 в 08:43
поделиться

Принятый ответ замечателен, однако, для упрощения, я хотел бы сделать все возможное, чтобы описать его по-другому. Надежда может помочь некоторым людям.

Когда вы сталкиваетесь с проблемой линеаризации, первый шаг - это рисование дерева иерархии классов и признаков. Для этого конкретного примера дерево иерархии будет примерно таким:

Второй шаг состоит в том, чтобы записать все линеаризация признаков и классов, которые мешают целевой задаче. Вам понадобятся все они в одном до последнего шага. Для этого вам нужно написать только путь к корню. Линеаризация признаков такова:

L(A) = A
L(C) = C -> B -> A
L(B) = B -> A
L(D) = D -> A

Третий шаг - написать линеаризацию проблемы. В этой конкретной задаче мы планируем решить линеаризацию

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.

Я знаю, что это не индивидуально глубокое концептуальное описание, но может помочь в качестве дополнения к концептуальному описанию, которое я предполагаю.

3
ответ дан Mehran 27 August 2018 в 08:43
поделиться

Паттерны свойств Scala, поэтому вы можете посмотреть их, добавив их по одному:

  1. Начать с new A => foo = "A"
  2. Стек with D => foo = "DA"
  3. Стек with C, который складывается with B => foo = "CBDA"
  4. Стек with B ничего не делает, потому что B уже уложен в C => foo = "CBDA"

Вот сообщение в блоге о том, как Scala решает проблему наследования алмазов.

11
ответ дан Noah 27 August 2018 в 08:43
поделиться

В дополнение к другим андерсерам вы можете найти пошаговое объяснение в результате фрагмента ниже

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>

1
ответ дан Odomontois 27 August 2018 в 08:43
поделиться

Процесс, с помощью которого 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

6
ответ дан optional 27 August 2018 в 08:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: