C3線性化

在計算機科學中，C3算法主要用在多重繼承中，確定子類應該繼承哪一個父類的方法。換句話說，C3超類線性化的輸出是確定性的方法決定次序（MRO：Method Resolution Order）。

**描述**
C3算法得名於它的實現符合三種重要性質：一致性擴展優先圖（EPG）、保留局部優先次序和適合單調性準則。
在1996年的OOPSLA會議上，發表的論文《Dylan的單調超類線性化》，首次提出了C3超類線性化。它在2012年1月被適配到了Open Dylan實現，跟從了一個增進的提議。Python 2.3、Raku、Parrot、Solidity和PGF/TikZ的面嚮對象語言模塊等，將它選作方法解析的默認算法。Perl 5語言從版本5.10.0起將它作為可選的、非默認的MRO。早期版本Perl 5有稱為Class::C3一個擴展實現曾發佈於CPAN。
Python創始人Guido van Rossum這樣總結C3超類線性化：“基本上，在C3背後的想法是，如果你寫下在複雜的類層級中繼承關係所施加的所有次序規則，這個算法將確定出滿足所有這些規則的這些類的一個單調次序。如果不能確定出這樣的次序，這個算法會失敗。”

**算法**
一個類的C3超類線性化，是這個類加上它的這些父類的線性化和這些父類自身的列表的唯一性歸併的總和。這些父類的列表作為給這個歸併過程的最後實際參數，保持了這些直接父類的局部優先次序。
完成父類的線性化和父類列表的歸併，選擇這些列表的頭部中第一個符合條件者，它不出現在任何這些列表的尾部（一個列表的除了第一個元素的所有元素）之中。注意一個良好頭部，可以同時出現為多個列表的第一個元素，但是禁止它出現在任何其他地方。將選擇的元素從以它作為頭部的所有列表中移除，並將它填加到輸出列表。重複選擇並移除良好頭部並擴展輸出列表的過程，直到所有餘下的列表被竭盡。如果在某一點上，因為所有餘下列表的頭部都出現在某個列表的尾部中，而沒有良好頭部可選，那麼由於在繼承層級中依賴的不一致次序，歸併過程不能計算下去，故而最初類的線性化不存在。
計算一個類的線性化的一種樸素分治法，可以採用遞歸算法來為歸併子例程找到父類的線性化。但是，這將在出現環狀類層級時導致無限循環遞歸。為了檢測這種環並打破無限遞歸，並重新利用前面計算的結果作為一種優化，遞歸調用應當通過緩存或記憶化的方式屏蔽重入以前的實際參數。
這個算法類似於找到拓撲次序。

**例子**
給定
一個C3線性化的依賴圖例子
class O
class A extends O
class B extends O
class C extends O
class D extends O
class E extends O
class K1 extends A, B, C
class K2 extends D, B, E
class K3 extends D, A
class Z extends K1, K2, K3
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的線性化的計算類似於A
L(C)  := C, O
L(D)  := D, O
L(E)  := E, O

L(K1) := K1 + merge(L(A), L(B), L(C), A, B, C)          // 首先找到K1的父類的線性化L(A)、L(B)和L(C)，接著將它們歸併於父類列表A, B, C
**K1 + merge(A, O, B, O, C, O, A, B, C)    // 類A是第一個歸併步驟的良好候選者，因為它只出現為第一個和最後一個列表的頭部元素。
**K1, A + merge(O, B, O, C, O, B, C)       // 類O不是第二個歸併步驟的良好候選者，因為它還出現在列表2和列表3的尾部中；但是類B是良好候選者
**K1, A, B + merge(O, O, C, O, C)          // 類C是良好候選者；類O仍出現在列表3的尾部中
**K1, A, B, C + merge(O, O, O)               // 最後，類O是有效候選者，這還竭盡了所有餘下的列表
**K1, A, B, C, O

L(K2) := K2 + merge(L(D), L(B), L(E), D, B, E)
**K2 + merge(D, O, B, O, E, O, D, B, E)    // 選擇D
**K2, D + merge(O, B, O, E, O, B, E)       // 不選O，選擇B
**K2, D, B + merge(O, O, E, O, E)          // 不選O，選擇E
**K2, D, B, E + merge(O, O, O)               // 選擇O
**K2, D, B, E, O

L(K3) := K3 + merge(L(D), L(A), D, A)
**K3 + merge(D, O, A, O, D, A)               // 選擇D
**K3, D + merge(O, A, O, A)                  // 不選O，選擇A
**K3, D, A + merge(O, O)                       // 選擇O
**K3, D, A, O

L(Z)  := Z + merge(L(K1), L(K2), L(K3), K1, K2, K3)
**Z + merge(K1, A, B, C, O, K2, D, B, E, O, K3, D, A, O, K1, K2, K3)    // 選擇K1
**Z, K1 + merge(A, B, C, O, K2, D, B, E, O, K3, D, A, O, K2, K3)        // 不選A，選擇K2
**Z, K1, K2 + merge(A, B, C, O, D, B, E, O, K3, D, A, O, K3)            // 不選A，不選D，選擇K3
**Z, K1, K2, K3 + merge(A, B, C, O, D, B, E, O, D, A, O)                  // 不選A，選擇D
**Z, K1, K2, K3, D + merge(A, B, C, O, B, E, O, A, O)                     // 選擇A
**Z, K1, K2, K3, D, A + merge(B, C, O, B, E, O, O)                        // 選擇B
**Z, K1, K2, K3, D, A, B + merge(C, O, E, O, O)                           // 選擇C
**Z, K1, K2, K3, D, A, B, C + merge(O, E, O, O)                           // 不選O，選擇E
**Z, K1, K2, K3, D, A, B, C, E + merge(O, O, O)                           // 選擇O
**Z, K1, K2, K3, D, A, B, C, E, O                                               // 完成

**用Python3展示例子**
首先，定義一個元類來允許對象通過名字簡明表示：
class Type(type):
def __repr__(cls):
return cls.__name__
class O(object, metaclass=Type): pass 
類O通過名字表示自身，而不採用默認表示形式：
>>> O
O
>>> class X: pass
... 
>>> X
下面定義一些基礎類：
class A(O): pass
class B(O): pass
class C(O): pass
class D(O): pass
class E(O): pass
接著構造這個繼承樹：
class K1(A, B, C): pass
class K2(D, B, E): pass
class K3(D, A): pass
class Z(K1, K2, K3): pass
然後就可看到結果：
>>> Z.__mro__
Z, K1, K2, K3, D, A, B, C, E, O, 

**用Raku展示例子**
Raku默認的對類使用C3線性化：
class A {}
class B {}
class C {}
class D {}
class E {}
class K1 is A is B is C {}
class K2 is D is B is E {}
class K3 is D is A {}
class Z is K1 is K2 is K3 {}
say Z.^mro; # OUTPUT: ((Z) (K1) (K2) (K3) (D) (A) (B) (C) (E) (Any) (Mu))
Any和Mu是所有Raku對象從其繼承的類型。

**參考文獻 ==