stringtranslate.com

Linealización C3

"En sistemas orientados a objetos con herencia múltiple, se debe utilizar algún mecanismo para resolver conflictos cuando se heredan diferentes definiciones de la misma propiedad de múltiples superclases". [1] La linealización de superclases C3 es un algoritmo utilizado principalmente para obtener el orden en el que se deben heredar los métodos en presencia de herencia múltiple . En otras palabras, el resultado de la linealización de superclases C3 es un Orden de Resolución de Métodos ( MRO ) determinista.

La linealización de la superclase C3 se llama C3 porque "es consistente con tres propiedades": [1]

Se publicó por primera vez en la conferencia OOPSLA de 1996, en un artículo titulado "A Monotonic Superclass Linearization for Dylan ". [1] Se adaptó a la implementación de Open Dylan en enero de 2012 [2] tras una propuesta de mejora. [3] Se ha elegido como el algoritmo predeterminado para la resolución de métodos en Python 2.3 (y más reciente), [4] [5] Raku , [6] Parrot , [7] Solidity y el módulo de programación orientada a objetos de PGF/TikZ . [8] También está disponible como una alternativa, MRO no predeterminada en el núcleo de Perl 5 a partir de la versión 5.10.0. [9] Existe una implementación de extensión para versiones anteriores de Perl 5 denominada Class::C3en CPAN . [10]

Guido van Rossum de Python resume la linealización de la superclase C3 de la siguiente manera: [11]

Básicamente, la idea detrás de C3 es que si se escriben todas las reglas de ordenamiento impuestas por las relaciones de herencia en una jerarquía de clases compleja, el algoritmo determinará un ordenamiento monótono de las clases que las satisfaga a todas. Si no se puede determinar dicho ordenamiento, el algoritmo fallará.

Descripción

La linealización de una superclase C3 es la suma de la clase más una fusión única de las linealizaciones de sus padres y una lista de los padres en sí. La lista de padres como último argumento del proceso de fusión conserva el orden de precedencia local de las clases de padres directos.

La fusión de las linealizaciones de los padres y la lista de padres se realiza seleccionando el primer encabezado de las listas que no aparece en la cola (todos los elementos de una lista excepto el primero) de ninguna de las listas. Tenga en cuenta que un encabezado bueno puede aparecer como el primer elemento en varias listas al mismo tiempo, pero está prohibido que aparezca en cualquier otro lugar. El elemento seleccionado se elimina de todas las listas en las que aparece como encabezado y se agrega a la lista de salida. El proceso de selección y eliminación de un encabezado bueno para extender la lista de salida se repite hasta que se agoten todas las listas restantes. Si en algún momento no se puede seleccionar ningún encabezado bueno, porque los encabezados de todas las listas restantes aparecen en cualquier cola de las listas, entonces la fusión es imposible de calcular debido a ordenaciones inconsistentes de dependencias en la jerarquía de herencia y no existe ninguna linealización de la clase original.

Un enfoque ingenuo de divide y vencerás para calcular la linealización de una clase puede invocar el algoritmo de forma recursiva para encontrar las linealizaciones de las clases padre para la subrutina de fusión. Sin embargo, esto dará como resultado una recursión de bucle infinito en presencia de una jerarquía de clases cíclica. Para detectar dicho ciclo y romper la recursión infinita (y reutilizar los resultados de cálculos anteriores como una optimización), la invocación recursiva debe estar protegida contra el reingreso de un argumento anterior por medio de una memoria caché o memorización .

Este algoritmo es similar a encontrar un orden topológico .

Ejemplo

Dado

Un gráfico de dependencia para el ejemplo de linealización C3.
 clase  O clase A extiende O clase B extiende O clase C extiende O clase D extiende O clase E extiende O clase K1 extiende C , A , B clase K3 extiende A , D clase K2 extiende B , D , E clase Z extiende K1 , K3 , K2                                           

La linealización de Z se calcula como

 L ( O ) := [ O ] // la linealización de O es trivialmente la lista singleton [O], porque O no tiene padres L ( A ) := [ A ] + merge ( L ( O ) , [ O ]) // la linealización de A es A más la fusión de las linealizaciones de sus padres con la lista de padres... = [ A ] + merge ([ O ] , [ O ]) = [ A , O ] // ...que simplemente antepone A a la linealización de su padre único L ( B ) := [ B , O ] // las linealizaciones de B, C, D y E se calculan de forma similar a la de A L ( C ) := [ C , O ] L ( D ) := [ D , O ] L ( E ) := [ E , O ] L ( K1 ) := [ K1 ] + merge ( L ( C ) , L ( B ) , L ( A ) , [ C , A , B ]) // primero, encuentre la linealización de los padres de K1, L(C), L(B) y L(A), y fusionelos con la lista padre [C, A, B] = [ K1 ] + merge ([ C , O ] , [ B , O ] , [ A , O ] , [ C , A , B ]) // la clase C es un buen candidato para el primer paso de fusión, porque solo aparece como el encabezado de la primera y la última lista = [ K1 , C ] + merge ([ O ] , [ B ,                                                                      O ] , [ A , O ] , [ A , B ]) // la clase O no es un buen candidato para el siguiente paso de fusión, porque también aparece en las colas de las listas 2 y 3, la clase B tampoco es buena; pero la clase A es un buen candidato = [ K1 , C , A ] + merge ([ O ] , [ B , O ] , [ O ] , [ B ]) // la clase B es un buen candidato; la clase O todavía aparece en la cola de la lista 2 = [ K1 , C , A , B ] + merge ([ O ] , [ O ] , [ O ]) // finalmente, la clase O es un candidato válido, lo que también agota todas las listas restantes = [ K1 , C , A , B , O ] L ( K3 ) := [ K3 ] + merge ( L ( A ) , L ( D ) , [ A , D ]) = [ K3 ] + merge ([ A , O ] , [ D , O ] , [ A , D ]) // select A = [ K3 , A ] + merge ([ O ] , [ D , O ] , [ D ]) // falla O, select D = [ K3 , A , D ] + merge ([ O ] , [ O ]) // select O = [ K3 , A , D , O ] L ( K2 ) := [                                                                             K2 ] + fusionar ( L ( B ) , L ( D ) , L ( E ) , [ B , D , E ]) = [ K2 ] + fusionar ([ B , O ] , [ D , O ] , [ E , O ] , [ B , D , E ]) // seleccionar B = [ K2 , B ] + fusionar ([ O ] , [ D , O ] , [ E , O ] , [ D , E ]) // falla O, seleccionar D = [ K2 , B , D ] + fusionar ([ O ] , [ O ] , [ E , O ] , [ E ]) // falla O, seleccionar E = [ K2 , B , D , E ] + fusionar ([ O ] , [ O ] , [ O ]) // seleccionar O = [ K2 , B , D , E , O ] L ( Z ) := [ Z ] + fusionar ( L ( K1 ) , L ( K3 ) , L ( K2 ) , [ K1 , K3 , K2 ]) = [ Z ] + fusionar ( [ K1 , C , A , B , O ] , [ K3                                                                               , A , D , O ] , [ K2 , B , D , E , O ] , [ K1 , K3 , K2 ]) // seleccione K1 = [ Z , K1 ] + fusionar ([ C , A , B , O ] , [ K3 , A , D , O ] , [ K2 , B , D , E , O ] , [ K3 , K2 ]) // seleccione C = [ Z , K1 , C ] + fusionar ([ A , B , O ] , [ K3 , A , D , O ] , [ K2 , B , D , E , O ] , [ K3 , K2 ]) // falla A, selecciona K3 = [ Z , K1 , C , K3 ] + fusionar ([ A , B , O ] , [ A , D , O ] , [ K2 , B , D , E , O ] , [ K2 ]) // seleccionar A = [ Z , K1 , C , K3 , A ] + fusionar ([ B , O ] , [ D , O ] , [ K2 , B , D , E , O ] , [ K2 ])                                                                                         // falla B, falla D, selecciona K2 = [ Z , K1 , C , K3 , A , K2 ] + fusionar ([ B , O ] , [ D , O ] , [ B , D , E , O ]) // selecciona B = [ Z , K1 , C , K3 , A , K2 , B ] + fusionar ([ O ] , [ D , O ] , [ D , E , O ]) // falla O, selecciona D = [ Z , K1 , C , K3 , A , K2 , B , D ] + fusionar ([ O ] , [ O ] , [ E , O ]) // falla O, selecciona E = [ Z , K1 , C , K3 , A , K2 , B , D , E ] + fusionar ([ O ] , [ O ] , [ O ]) // seleccione O = [ Z , K1 , C , K3 , A , K2 , B , D , E , O ] // listo.                                                                           

Ejemplo demostrado en Python 3

Primero, una metaclase para permitir una representación corta de los objetos por nombre en lugar del valor REPR de la clase predeterminada:

clase  Tipo ( type ):  def  __repr__ ( cls ):  # Hará que repr(O) sea "O" en lugar de "<__main__.O>",  # y lo mismo para cualquier subclase de O.  return  cls . __name__clase  O ( objeto ,  metaclase = Tipo ):  pasar

A continuación, definimos nuestras clases base:

Clase  A ( O ):  aprobadoClase  B ( O ):  aprobadoClase  C ( O ):  aprobadoClase  D ( O ):  aprobadoClase  E ( O ):  aprobado

Luego construimos el árbol de herencia:

Clase  K1 ( C ,  A ,  B ):  aprobadoClase  K3 ( A ,  D ):  aprobadoClase  K2 ( B ,  D ,  E ):  aprobadoClase  Z ( K1 ,  K3 ,  K2 ):  aprobado

Y ahora:

>>> Z . mro () [Z, K1, C, K3, A, K2, B, D, E, O, <clase 'objeto'>]

Ejemplo demostrado en Raku

Raku utiliza la linealización C3 para las clases de forma predeterminada:

clase  A {} clase  B {} clase  C {} clase  D {} clase  E {} clase  K1  es  C  es  A  es  B {} clase  K3  es  A  es  D {} clase  K2  es  B  es  D  es  E {} clase  Z  es  K1  es  K3  es  K2 {} digamos  Z .^ mro ; # SALIDA: ((Z) (K1) (C) (K3) (A) (K2) (B) (D) (E) (Cualquiera) (Mu))

( Anyy Muson los tipos de los que heredan todos los objetos Raku, por lo que Anyreemplaza a O)

Referencias

  1. ^ abc Kim Barrett, Bob Cassels, Paul Haahr, David A. Moon, Keith Playford, P. Tucker Withington (28 de junio de 1996). "Una linealización de superclase monótona para Dylan". Actas de la conferencia OOPSLA '96 . ACM Press . págs. 69–82. CiteSeerX  10.1.1.19.3910 . doi :10.1145/236337.236343. ISBN . 0-89791-788-X.{{cite conference}}: CS1 maint: multiple names: authors list (link)
  2. ^ Noticia en opendylan.org
  3. ^ Propuesta de mejora de Dylan 3: Linealización de la superclase C3
  4. ^ Uso de C3 MRO en Python 2.3
  5. ^ Tutorial para aplicaciones prácticas de linealización C3 usando Python
  6. ^ Uso del MRO C3 en Perl 6
  7. ^ "Parrot utiliza C3 MRO". Archivado desde el original el 8 de febrero de 2007. Consultado el 6 de diciembre de 2006 .
  8. ^ Tantau, hasta (29 de agosto de 2015). El manual de TikZ y PGF (PDF) (3.1.9a ed.). pag. 1062 . Consultado el 15 de mayo de 2021 .
  9. ^ C3 MRO disponible en Perl 5.10 y versiones más recientes
  10. ^ Extensión de Perl 5 para C3 MRO en CPAN
  11. ^ van Rossum, Guido (23 de junio de 2010). «Orden de resolución de métodos». La historia de Python . Consultado el 18 de enero de 2018 .