輸入參數化
Q# 支援類型參數化作業和函式。
Q#標準連結庫會大量使用類型參數化可呼叫專案,以提供實用的抽象概念主機,包括類似 Mapped
和 Fold
的函式,以及功能語言中熟悉的函式。
若要說明類型參數化的概念,請參考 Mapped
函式的範例,此範例會將指定函式套用至陣列中的每個值,並傳回含有計算值的新陣列。 您可以完全描述這項功能,而不需指定輸入和輸出陣列的項目類型。 由於確切類型不會變更函式的Mapped
實作,因此應該可以針對任意專案類型定義此實作;我們想要定義處理站或範本,以針對輸入和輸出數位中的專案指定具體類型,傳回對應的函式實作。 此概念是以類型參數的形式正規化。
具體化
任何作業或函式宣告都可以指定一或多個類型參數,這些參數可用來做為可呼叫之輸入或輸出的類型或部分類型,或兩者。 例外狀況是進入點,必須是具象的,而且不能是類型參數化。 類型參數名稱開頭為刻度 ('),且可能會多次出現在輸入和輸出類型中。 所有對應至可呼叫簽章中相同類型參數的引數都必須為相同類型。
型別參數化可呼叫性必須經過串連,也就是說,必須先提供必要的型別自變數,才能將它指派或傳遞為自變數,讓所有型別參數都可以取代為具體型別。 如果類型是其中一個內建類型、使用者定義型別,或它是目前範圍內的具體類型,則類型會被視為具象。 下列範例說明類型在目前範圍內為實體所代表的意義,且在以下更詳細地說明:
function Mapped<'T1, 'T2> (
mapper : 'T1 -> 'T2,
array : 'T1[]
) : 'T2[] {
mutable mapped = new 'T2[Length(array)];
for (i in IndexRange(array)) {
set mapped w/= i <- mapper(array[i]);
}
return mapped;
}
function AllCControlled<'T3> (
ops : ('T3 => Unit)[]
) : ((Bool,'T3) => Unit)[] {
return Mapped(CControlled<'T3>, ops);
}
CControlled
函式是在 Microsoft.Quantum.Canon
命名空間中定義。 它會採用 類型'TIn => Unit
為 自變數的作業,並傳回套(Bool, 'TIn) => Unit
用原始作業的新作業op
,前提是傳統位 (類型 Bool
) 設定為 true;這通常稱為傳統控制的 版本op
。
函式 Mapped
會接受任意項目類型的 'T1
數位做為自變數、將指定的 mapper
函式套用至每個專案,並傳回包含對應專案之型 'T2[]
別的新數位。 此函式是在 Microsoft.Quantum.Array
命名空間中定義的。 基於此範例的目的,我們會將類型參數編號,以免兩個函式中的類型參數同名時造成說明混淆。 其實並不需要這麼做,因為不同可呼叫項目的類型參數可能會同名,但只有在該可呼叫項目的定義內會顯示選擇的相關名稱。
函式 AllCControlled
會使用作業的陣列並傳回新陣列,其中包含這些作業的傳統方式控制版本。
Mapped
的呼叫會將其 'T1
類型參數解析為 'T3 => Unit
,並將其 'T2
類型參數解析為 (Bool,'T3) => Unit
。 編譯器會根據指定引數類型來推斷解析的類型引數。 我們假設這些引數是由呼叫運算式的引數「隱含」定義。 您也可以明確指定型別自變數,如同在同一行中所做的 CControlled
。 當無法推斷類型引數時,就需要明確的 CControlled<'T3>
實體化。
'T3
類型在 AllCControlled
的內容中是實體,因為 AllCControlled
的每個「叫用」都已知此類型。 這表示一旦程式進入點無法進行型別參數化即為已知,因此每個呼叫AllCControlled
的具體類型'T3
都是 ,因此可以產生適合該特定類型解析的實作。 一旦已知程序進入點,就可以在編譯時期消除類型參數的所有使用方式。 我們將此程序稱為「單態化」。
需要一些限制,以確保這確實可以在編譯時期完成,而不是只在運行時間完成。
限制
請思考一下下列範例:
operation Foo<'TArg> (
op : 'TArg => Unit,
arg : 'TArg
) : Unit {
let cbit = RandomInt(2) == 0;
Foo(CControlled(op), (cbit, arg));
}
忽略的 Foo
叫用會導致無限迴圈,其用途為圖例。
Foo
使用已傳入之原始作業的傳統控制版本以及包含隨機傳統位的 Tuple,以及原始自變數以外的原始自 op
變數,叫用本身。
針對遞迴中的每個反覆項目,會將下一個呼叫的 'TArg
類型參數解析為 (Bool, 'TArg)
,其中 'TArg
是目前呼叫的類型參數。 具體而言,假設 Foo
是使用 H
作業和 Qubit
類型的引數 arg
來叫用。
Foo
接著會使用類型引數 (Bool, Qubit)
叫用自身,並導致使用類型引數 (Bool, (Bool, Qubit))
來叫用 Foo
,依此類推。 顯然地,在此情況下,即無法在編譯時間將 Foo
單態化。
其他限制適用於只涉及類型參數化可呼叫物件的呼叫圖形中的迴圈。 在周遊循環之後,每個可呼叫項目都必須使用同一組類型引數來叫用。
注意
在迴圈中,每個可呼叫的迴圈可能會比較不嚴格,而且需要有有限的循環數目,之後就會使用原始類型自變數集叫用,例如下列函式的案例:
function Bar<'T1,'T2,'T3>(a1:'T1, a2:'T2, a3:'T3) : Unit{
Bar<'T2,'T3,'T1>(a2, a3, a1);
}
為了簡單起見,我們會實施更嚴格的需求。 請注意,對於包含至少一個具象可呼叫且不含任何型別參數的迴圈,這類可呼叫者可確保該週期內的型別參數化可呼叫一律會使用一組固定的類型自變數來呼叫。