Share via


Åtgärder och funktioner

Som beskrivs mer detaljerat i beskrivningen av kvantbitsdatatypen körs kvantberäkningar i form av sidoeffekter av åtgärder som stöds internt på den riktade kvantprocessorn. Dessa är i själva verket de enda biverkningarna i Q#. Eftersom alla typer är oföränderliga finns det inga biverkningar som påverkar ett värde som uttryckligen representeras i Q#. Så länge en implementering av en viss anropningsbar inte direkt eller indirekt anropar någon av dessa internt implementerade åtgärder, genererar dess körning alltid samma utdata, givet samma indata.

Q# gör att du uttryckligen kan dela upp sådana rent deterministiska beräkningar i funktioner. Eftersom uppsättningen med instruktioner som stöds internt inte är fast och inbyggd i själva språket, utan snarare är helt konfigurerbar och uttrycks som ett Q# bibliotek, garanteras determinism genom att kräva att funktioner bara kan anropa andra funktioner och inte kan anropa några åtgärder. Dessutom representeras inbyggda instruktioner som inte är deterministiska, det vill säga eftersom de påverkar kvanttillståndet som åtgärder. Med dessa två begränsningar kan funktioner utvärderas så snart deras indatavärde är känt och i princip aldrig behöver utvärderas mer än en gång för samma indata.

Q# därför skiljer mellan två typer av anropbara objekt: åtgärder och funktioner. Alla anropbara objekt tar ett enda argument (potentiellt tuppelvärde) som indata och genererar ett enda värde (tuppel) som utdata. Syntaktiskt uttrycks åtgärdstypen som <TIn> => <TOut> is <Char>, där <TIn> ska ersättas av argumenttypen, <TOut> ska ersättas av returtypen och <Char> ersättas med åtgärdsegenskaperna. Om inga egenskaper behöver anges förenklas syntaxen till <TIn> => <TOut>. På samma sätt uttrycks funktionstyper som <TIn> -> <TOut>.

Förutom denna determinismgaranti finns det liten skillnad mellan åtgärder och funktioner. Båda är förstklassiga värden som kan skickas fritt. De kan användas som returvärden eller argument till andra anropbara objekt, som du ser i följande exempel:

function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit {
    return PowImpl(op, pow, _);
}

Båda kan instansieras baserat på en typparametriserad definition, till exempel typen parametriserad funktion Pow ovan, och de kan tillämpas delvis som i -instruktionen return i exemplet.

Driftegenskaper

Förutom informationen om indata- och utdatatypen innehåller åtgärdstypen information om egenskaperna för en åtgärd. Den här informationen beskriver till exempel vilka funktorer som stöds av åtgärden. Dessutom innehåller den interna representationen även optimeringsrelevent information som härleds av kompilatorn.

Egenskaperna för en åtgärd är en uppsättning fördefinierade och inbyggda etiketter. De uttrycks i form av ett specialuttryck som är en del av typsignaturen. Uttrycket består antingen av en av de fördefinierade uppsättningarna etiketter eller av en kombination av egenskapsuttryck via en binär operator som stöds.

Det finns två fördefinierade uppsättningar, Adj och Ctl.

  • Adj är uppsättningen som innehåller en enda etikett som anger att en åtgärd är angränsande, vilket innebär att den Adjoint stöder functor och den tillämpade kvanttransformeringen kan "ångras", det vill säga den kan inverteras.
  • Ctl är uppsättningen som innehåller en enda etikett som anger att en åtgärd är kontrollerbar, vilket innebär att den Controlled stöder funktor och dess körning kan villkoras av tillståndet för andra kvantbitar.

De två operatorer som stöds som en del av egenskapsuttrycken är den angivna unionen + och den angivna skärningspunkten *. I EBNF,

    predefined = "Adj" | "Ctl";
    characteristics = predefined 
        | "(", characteristics, ")" 
        | characteristics ("+"|"*") characteristics;

Som man kan förvänta sig * har högre prioritet än + och båda är vänster-associativa. Typen av en enhetsåtgärd uttrycks till exempel som <TIn> => <TOut> is Adj + Ctl, där <TIn> ska ersättas med typen av åtgärdsargument och <TOut> ersättas med typen av det returnerade värdet.

Anteckning

Att ange egenskaperna hos en operation i denna form har två stora fördelar. För det första kan nya etiketter introduceras utan exponentiellt många språknyckelord för alla kombinationer av etiketter. Kanske ännu viktigare, att använda uttryck för att ange egenskaperna för en åtgärd stöder också parameteriseringar över åtgärdsegenskaper i framtiden.