Jump to content

Линеаризация C3

«В объектно-ориентированных системах с множественным наследованиемдолжен использоваться какой-то механизм для разрешения конфликтов при наследовании разных определений одного и того же свойства.из нескольких суперклассов». [1] Линеаризация суперкласса C3 — это алгоритм, используемый в первую очередь для получения порядка, в котором методы должны наследоваться при наличии множественного наследования . Другими словами, результатом линеаризации суперкласса C3 является детерминированный порядок разрешения метода ( MRO ).

Линеаризация суперкласса C3 называется C3, потому что она «согласуется с тремя свойствами»: [1]

  • последовательный расширенный граф приоритетов ,
  • сохранение порядка локального приоритета и
  • соответствие критерию монотонности.

Впервые он был опубликован на конференции OOPSLA в 1996 году в статье под названием «Монотонная линеаризация суперкласса для Дилана ». [1] Он был адаптирован к реализации Open Dylan в январе 2012 года. [2] после предложения по улучшению. [3] Он был выбран в качестве алгоритма по умолчанию для разрешения методов в Python 2.3 (и новее). [4] [5] Раку , [6] Попугай , [7] Solidity и PGF/TikZ . модуль объектно-ориентированного программирования [8] Он также доступен в качестве альтернативы MRO, отличной от используемой по умолчанию, в ядре Perl 5, начиная с версии 5.10.0. [9] Реализация расширения для более ранних версий Perl 5 под названием Class::C3 существует на CPAN . [10]

из Python Гвидо ван Россум резюмирует линеаризацию суперкласса C3 следующим образом: [11]

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

Описание

[ редактировать ]

Линеаризация суперкласса C3 класса представляет собой сумму класса плюс уникальное слияние линеаризации его родителей и самого списка родителей. Список родителей в качестве последнего аргумента процесса слияния сохраняет локальный порядок приоритета прямых родительских классов.

Объединение линеаризации родителей и списка родителей выполняется путем выбора первого заголовка списков, который не появляется в хвосте (все элементы списка, кроме первого) ни в одном из списков. Обратите внимание, что хорошая голова может появиться первым элементом в нескольких списках одновременно, но ей запрещено появляться где-либо еще. Выбранный элемент удаляется из всех списков, где он отображается в качестве заголовка, и добавляется в выходной список. Процесс выбора и удаления хорошей головки для расширения выходного списка повторяется до тех пор, пока не будут исчерпаны все оставшиеся списки. Если в какой-то момент ни один хороший заголовок не может быть выбран, поскольку главы всех оставшихся списков появляются в каком-либо одном хвосте списков, то слияние невозможно вычислить из-за непоследовательного порядка зависимостей в иерархии наследования и отсутствия линеаризации исходного списка. класс существует.

Наивный подход к вычислению линеаризации класса по принципу «разделяй и властвуй» может рекурсивно вызывать алгоритм для поиска линеаризации родительских классов для подпрограммы слияния. Однако это приведет к бесконечно зацикленной рекурсии при наличии циклической иерархии классов. Чтобы обнаружить такой цикл и разорвать бесконечную рекурсию (и повторно использовать результаты предыдущих вычислений в качестве оптимизации), рекурсивный вызов должен быть защищен от повторного входа предыдущего аргумента с помощью кэша или мемоизации .

Этот алгоритм аналогичен поиску топологического порядка .

Данный

График зависимости для примера линеаризации C3.
 класс   O   класс   A   расширяет   O   класс   B   расширяет   O   класс   C   расширяет   O   класс   D   расширяет   O   класс   E   расширяет   O   класс   K1   расширяет   C  ,   A  ,   B   класс   K3   расширяет   A  ,   D   класс   K2   расширяет   B  ,   D  ,   E   класс   Z   расширяет   K1  ,   K3  ,   К2 

линеаризация Z вычисляется как

 L  (  O  )    :=   [  O  ]                                                  // линеаризация O тривиально представляет собой одноэлементный список [O], поскольку у O нет родителей    L  (  A  )    :=   [  A  ]   +   merge  (  L  (  O  )  ,   [  O  ])                               // линеаризация A — это A плюс слияние линеаризаций его родителей со списком родителей...          =   [  A  ]   +   merge  ([  O  ]  ,   [  O  ])          =   [  A  ,   O  ]                                               // ... который просто добавляет A к линеаризации своего единственного родителя    L  (  B  )    :=   [  B  ,   O  ]                                               // линеаризации B, C, D и E вычисляются аналогично линеаризации AL   (  C  )  :    =   [  C  ,   O  ]   L  (  D  )    :=   [  D  ,   O  ]   L  (  E  )    :=   [  E  ,   O  ]    L  (  K1  )   :=   [  K1  ]   +   слияние  (  L  (  C  )  ,   L  (  B  )  ,   L  (  A  )  ,   [  C  ,   A  ,   B  ])            // сначала находим линеаризацию родителей K1, L(C), L(B) и L(A), и объединяем их со списком родителей [C, A, B]          =   [  K1  ]   +   merge  ([  C  ,   O  ]  ,   [  B  ,   O  ]  ,   [  A  ,   O  ]  ,   [  C  ,   A  ,   B  ])      // класс C является хорошим кандидатом для первого шага слияния, потому что он появляется только как глава первого и последнего списков          =   [  K1  ,   C  ]   +   merge  ([  O  ]  ,   [  B  ,   O  ]  ,   [  A  ,   O  ]  ,   [  A  ,   B  ])         // класс O не является хорошим кандидатом на следующий шаг слияния, поскольку он также появляется в хвостах списков 2 и 3, класс B тоже не годится; но класс A — хороший кандидат          =   [  K1  ,   C  ,   A  ]   +   merge  ([  O  ]  ,   [  B  ,   O  ]  ,   [  O  ]  ,   [  B  ])            // класс B — хороший кандидат; класс O все еще появляется в конце списка 2         =   [  K1  ,   C  ,   A  ,   B  ]   +   merge  ([  O  ]  ,   [  O  ]  ,   [  O  ])                 // наконец, класс O является допустимым кандидатом, который также исчерпывает все оставшиеся списки          =   [  K1  ,   C  ,   A  ,   B  ,   O  ]    L  (  K3  )   :=   [  K3  ]   +   слияние  (  L  (  A  )  ,   L  (  D  )  ,   [  A  ,   D  ])          =   [  K3  ]   +   слияние  ([  A  ,   O  ]  ,   [  D  ,   O  ]  ,   [  A  ,   D  ])                 // выбираем A          =   [  K3  ,   A  ]   +   объединяем  ([  O  ]  ,   [  D  ,   O  ]  ,   [  D  ])                    // неудачно O, выбираем D          =   [  K3  ,   A  ,   D  ]   +   объединить  ([  O  ]  ,   [  O  ])                         // выбрать O          =   [  K3  ,   A  ,   D  ,   O  ]    L  (  K2  )   :=   [  K2  ]   +   объединить  (  L  (  B  )  ,   L  (  D  )  ,   L  (  E  )  ,   [  B  ,   D  ,   E  ])          =   [  K2  ]   +   merge  ([  B  ,   O  ]  ,   [  D  ,   O  ]  ,   [  E  ,   O  ]  ,   [  B  ,   D  ,   E  ])      // выбираем B          =   [  K2  ,   B  ]   +   merge  ([  O  ]  ,   [  D  ,   O  ]  ,   [  E  ,   O  ]  ,   [  D  ,   E  ])         // неудача O, выберите D          =   [  K2  ,   B  ,   D  ]   +   merge  ([  O  ]  ,   [  O  ]  ,   [  E  ,   O  ]  ,   [  E  ])            // неудача O, выберите E          =   [  K2  ,   B  ,   D  ,   E  ]   +   merge  ([  O  ]  ,   [  O  ]  ,   [  O  ])                 // выберите O          =   [  K2  ,   B  ,   D  ,   E  ,   O  ]    L  (  Z  )    :=   [  Z  ]   +   объединить  (  L  (  K1  )  ,   L  (  K3  )  ,   L  (  K2  )  ,   [  K1  ,   K3  ,   K2  ])          =   [  Z  ]   +   объединить  ([  K1  ,   C  ,   A  ,   B  ,   O  ]  ,   [  K3  ,   A  ,   D  ,   O  ]  ,   [  K2  ,   B  ,   D  ,   E  ,   O  ]  ,   [  K1  ,   K3  ,   K2  ])      // выбираем K1          =   [  Z  ,   K1  ]   +   merge  ([  C  ,   A  ,   B  ,   O  ]  ,   [  K3  ,   A  ,   D  ,   O  ]  ,   [  K2  ,   B  ,   D  ,   E  ,   O  ]  ,   [  K3  ,   K2  ])          // выбираем C          =   [  Z  ,   K1  ,   C  ]   +   merge  ([  A  ,   B  ,   O  ]  ,   [  K3  ,   A  ,   D  ,   O  ]  ,   [  K2  ,   B  ,   D  ,   E  ,   O  ]  ,   [  K3  ,   K2  ])          // неудача A, выберите K3          =   [  Z  ,   K1  ,   C  ,   K3  ]   +   объединить  ([  A  ,   B  ,   O  ]  ,   [  A  ,   D  ,   O  ]  ,   [  K2  ,   B  ,   D  ,   E  ,   O  ]  ,   [  K2  ])              / / select A          =   [  Z  ,   K1  ,   C  ,   K3  ,   A  ]   +   merge  ([  B  ,   O  ]  ,   [  D  ,   O  ]  ,   [  K2  ,   B  ,   D  ,   E  ,   O  ]  ,   [  K2  ])                 // неудача B , провалить D, выбрать K2          =   [  Z  ,   K1  ,   C  ,   K3  ,   A  ,   K2  ]   +   объединить  ([  B  ,   O  ]  ,   [  D  ,   O  ]  ,   [  B  ,   D  ,   E  ,   O  ])                       // выбрать B         =   [  Z  ,   K1  ,   C  ,   K3  ,   A  ,   K2  ,   B  ]   +   merge  ([  O  ]  ,   [  D  ,   O  ]  ,   [  D  ,   E  ,   O  ])                          // неудача O, выберите D          =   [  Z  ,   K1  ,   C  ,   K3  ,   A  ,   K2  ,   B  ,   D  ]   +   merge  ([  O  ]  ,   [  O  ]  ,   [  E  ,   O  ])                             // неудача O, выберите E          =   [  Z  ,   K1  ,   C  ,   K3  ,   A  ,   K2  ,   B  ,   D  ,   E  ]   +   merge  ([  O  ]  ,   [  O  ]  ,   [  O  ])                             // выбираем O          =   [  Z  ,   K1  ,   C  ,   K3  ,   A  ,   K2  ,   B  ,   D  ,   E  ,   O  ]                                                 // готово . 

Пример, продемонстрированный на Python 3

[ редактировать ]

Во-первых, метакласс, позволяющий кратко представлять объекты по имени вместо значения REPR класса по умолчанию:

class   Type  (  type  ):      def   __repr__  (  cls  ):          # Сделаем так, чтобы repr(O) был "O" вместо "<__main__.O>",          # и то же самое для любых подклассов O.          return   cls  .  __name__  класс   O  (  объект  ,   метакласс  =  Тип  ):   пройти 

Далее мы определяем наши базовые классы:

класс   A  (  O  ):   пройти  класс   B  (  O  ):   пройти  класс   C  (  O  ):   пройти  класс   D  (  O  ):   пройти  класс   E  (  O  ):   пройти 

Затем строим дерево наследования:

класс   K1  (  C  ,   A  ,   B  ):   пройти  класс   K3  (  A  ,   D  ):   пройти  класс   K2  (  B  ,   D  ,   E  ):   пройти  класс   Z  (  K1  ,   K3  ,   K2  ):   пройти 

И теперь:

>>>  З.  mro  ()  [Z, K1, C, K3, A, K2, B, D, E, O, <класс 'объект'>] 

Пример, продемонстрированный в Раку

[ редактировать ]

Раку по умолчанию использует линеаризацию C3 для классов:

класс   А  {} класс   Б  {} класс   С  {} класс   Д  {} класс   Е  {} класс   K1   — это   C,   это   A,   это   B  {} класс   K3   —   A   —   D  {} класс   K2   — это   B   — это   D   — это   E  {} класс   Z   есть   K1   есть   K3   есть   K2  {} скажи   Z  .^  mro  ;  # ВЫХОД: ((Z) (К1) (С) (К3) (А) (К2) (Б) (D) (Е) (Любой) (Мю)) 

( Any и Mu являются типами, от которых наследуются все объекты Raku, поэтому Any стоит на месте О)

  1. ^ Jump up to: а б с Ким Барретт, Боб Касселс, Пол Хаар, Дэвид А. Мун, Кейт Плейфорд, П. Такер Витингтон (28 июня 1996 г.). «Монотонная линеаризация суперкласса для Дилана». Материалы конференции OOPSLA '96 . АКМ Пресс . стр. 69–82. CiteSeerX   10.1.1.19.3910 . дои : 10.1145/236337.236343 . ISBN  0-89791-788-Х . {{cite conference}}: CS1 maint: несколько имен: список авторов ( ссылка )
  2. ^ Новость на opendylan.org
  3. ^ Предложение Дилана по улучшению 3: линеаризация суперкласса C3
  4. ^ Использование C3 MRO в Python 2.3
  5. ^ Учебное пособие по практическому применению линеаризации C3 с использованием Python.
  6. ^ Использование C3 MRO в Perl 6.
  7. ^ «Попугай использует C3 MRO» . Архивировано из оригинала 8 февраля 2007 г. Проверено 6 декабря 2006 г.
  8. ^ Тантау, Тилль (29 августа 2015 г.). Руководство TikZ и PGF (PDF) (изд. 3.1.9a). п. 1062 . Проверено 15 мая 2021 г.
  9. ^ C3 MRO доступен в Perl 5.10 и новее.
  10. ^ Расширение Perl 5 для C3 MRO на CPAN
  11. ^ ван Россум, Гвидо (23 июня 2010 г.). «Порядок разрешения метода» . История Питона . Проверено 18 января 2018 г.
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: bfb7f37dded1e1cf808dde762e4bef67__1712694720
URL1:https://arc.ask3.ru/arc/aa/bf/67/bfb7f37dded1e1cf808dde762e4bef67.html
Заголовок, (Title) документа по адресу, URL1:
C3 linearization - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)