Megosztás a következőn keresztül:


Típus-paraméterezés

Q# támogatja a típusparaméteres műveleteket és függvényeket. A Q# standard kódtárak nagy igénybevételt jelentenek a típusparametrizált hívhatókkal, hogy számos hasznos absztrakciót nyújtsanak, beleértve az olyan függvényeket is, mint Mapped a és Fold a funkcionális nyelvekből ismerős függvények.

A típusparaméterezés fogalmának motiválásához vegye figyelembe a függvény Mappedpéldáját, amely egy adott függvényt alkalmaz egy tömb minden értékére, és egy új tömböt ad vissza a kiszámított értékekkel. Ez a funkció tökéletesen leírható anélkül, hogy meg kellene adnia a bemeneti és kimeneti tömbök elemtípusait. Mivel a pontos típusok nem változtatják meg a függvény Mappedimplementációját, célszerű ezt az implementációt tetszőleges elemtípusokhoz definiálni. Olyan gyárat vagy sablont szeretnénk definiálni, amely a bemeneti és kimeneti tömb elemeinek konkrét típusait figyelembe véve a megfelelő függvény implementációját adja vissza. Ez a fogalma típusparaméterek formájában formalizálódik.

Összefűzés

Bármely művelet vagy függvény deklaráció megadhat egy vagy több típusparamétert, amelyek használhatók a hívható bemenetének vagy kimenetének típusaként vagy részeként, vagy mindkettőként. A kivételek belépési pontok, amelyeknek konkrétnak kell lenniük, és nem lehet típusparametrizáltak. A típusparaméterek neve egy osztással (') kezdődik, és többször is megjelenhet a bemeneti és kimeneti típusokban. A hívható aláírás azonos típusú paraméterének megfelelő argumentumoknak azonos típusúnak kell lenniük.

A típusparametrizált hívhatót össze kell fűzni, azaz meg kell adni a szükséges típusargumentumokat, mielőtt argumentumként hozzárendelhető vagy átadható lenne, hogy minden típusparaméter lecserélhető legyen konkrét típusokkal. A típus akkor tekinthető konkrétnak, ha az egyik beépített típus, felhasználó által definiált típus, vagy ha a jelenlegi hatókörön belül konkrét. Az alábbi példa azt szemlélteti, hogy mit jelent egy típus konkrétnak lenni a jelenlegi hatókörön belül, és az alábbiakban részletesebben ismertetjük:

    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); 
    }

A függvény CControlled a Microsoft.Quantum.Canon névtérben van definiálva. Egy típusú 'TIn => Unit műveletet op vesz fel argumentumként, és egy új típusú műveletet (Bool, 'TIn) => Unit ad vissza, amely az eredeti műveletet alkalmazza, feltéve, hogy a klasszikus bit (típusBool) értéke igaz; ezt gyakran a klasszikusan szabályozott verziónak opnevezik.

A függvény Mapped argumentumként egy tetszőleges elemtípusból 'T1 álló tömböt vesz fel, az adott mapper függvényt alkalmazza az egyes elemekre, és egy új típusú 'T2[] tömböt ad vissza, amely a megfeleltetett elemeket tartalmazza. A névtérben Microsoft.Quantum.Array van definiálva. A példa alkalmazásában a típusparaméterek számozottak, hogy a vitafórum ne legyen zavaróbb azáltal, hogy mindkét függvényben ugyanazt a nevet adja a típusparamétereknek. Ez nem szükséges; a különböző hívhatók típusparamétereinek ugyanaz a neve, és a választott név csak a hívható definíciójában látható és releváns.

A függvény AllCControlled egy művelettömböt vesz fel, és egy új tömböt ad vissza, amely ezeknek a műveleteknek a klasszikusan szabályozott verzióit tartalmazza. A hívás Mapped feloldja a típusparamétert 'T1 a értékre 'T3 => Unit, a típusparaméterét 'T2 pedig a következőre (Bool,'T3) => Unit: . A feloldási típus argumentumait a fordító a megadott argumentum típusa alapján következteti. Azt mondjuk, hogy implicit módon a híváskifejezés argumentuma határozza meg őket. A típusargumentumok explicit módon is megadhatók, ahogy az ugyanabban a sorban történik CControlled . Az explicit összefűzésre CControlled<'T3> akkor van szükség, ha a típusargumentumok nem következtethetők ki.

A típus 'T3 konkrét a kontextusábanAllCControlled, mivel ismert minden egyes meghívásaAllCControlled. Ez azt jelenti, hogy amint a program belépési pontja - amely nem lehet típus-parametrized - ismert, ugyanúgy a konkrét típus 'T3 minden egyes híváshoz AllCControlled, hogy megfelelő implementációt lehessen létrehozni az adott típusfeloldáshoz. Miután a program belépési pontja ismert, a típusparaméterek összes használata kiküszöbölhető fordítási időpontban. Ezt a folyamatot monomorfizálásnak nevezzük.

Bizonyos korlátozásokra van szükség annak biztosításához, hogy ez fordítási időben is elvégezhető legyen, nem pedig csak futásidőben.

Korlátozások

Tekintse meg a következő példát:

    operation Foo<'TArg> (
        op : 'TArg => Unit,
        arg : 'TArg
    ) : Unit {

        let cbit = RandomInt(2) == 0;
        Foo(CControlled(op), (cbit, arg));        
    } 

Figyelmen kívül hagyva, hogy a meghívás Foo végtelen hurkot eredményez, az illusztráció céljára szolgál. Foo meghívja magát az eredeti művelet op klasszikusan ellenőrzött verziójával, amely át lett adva, valamint egy véletlenszerű klasszikus bitet az eredeti argumentum mellett.

A rekurzió minden iterációja esetén a következő hívás típusparamétere 'TArg a következőre (Bool, 'TArg)lesz feloldva, ahol 'TArg az aktuális hívás típusparamétere. Tegyük fel, hogy Foo a művelettel H és egy típusú Qubitargumentummal arg hívjuk meg. Fooezután meghívja magát egy típusargumentummal(Bool, Qubit), amely egy típusargumentummal (Bool, (Bool, Qubit))és Foo így tovább. Ebben az esetben Foo egyértelműen nem lehet monomorfizálni fordításkor.

További korlátozások vonatkoznak a hívási gráf azon ciklusaira, amelyek csak típusparametrizált hívható elemeket foglalnak magukban. A ciklus bejárása után minden meghívhatót ugyanazzal a típusargumentumkészlettel kell meghívni.

Megjegyzés

Lehet, hogy kevésbé korlátozó, és megköveteli, hogy a ciklus minden hívható esetében véges számú ciklus legyen, amely után az eredeti típusargumentumokkal hívják meg, például a következő függvény esetében:

   function Bar<'T1,'T2,'T3>(a1:'T1, a2:'T2, a3:'T3) : Unit{
       Bar<'T2,'T3,'T1>(a2, a3, a1);
   }

Az egyszerűség kedvéért a szigorúbb követelményt kell kikényszeríteni. Vegye figyelembe, hogy az olyan ciklusok esetében, amelyekben legalább egy konkrét hívható típusparaméter nélkül van, az ilyen hívhatóak biztosítják, hogy az adott cikluson belüli típusparametrizált hívhatókat mindig rögzített típusargumentumokkal hívja meg a rendszer.