Wywoływalne deklaracje

Deklaracje wywoływane lub wywoływane deklaracje zadeklarowane w zakresie globalnym są domyślnie widoczne publicznie; oznacza to, że mogą być używane w dowolnym miejscu w tym samym projekcie i w projekcie, który odwołuje się do zestawu, w którym są zadeklarowane. Modyfikatory dostępu umożliwiają ograniczenie ich widoczności tylko do bieżącego zestawu, dzięki czemu szczegóły implementacji można później zmienić bez przerywania kodu, który opiera się na określonej bibliotece.

Q# obsługuje dwa rodzaje wywołań: operacje i funkcje. W temacie Operacje i funkcje opracować różnice między nimi. Q# Obsługuje również definiowanie szablonów; na przykład implementacje sparametryzowane typu dla określonego wywołania. Aby uzyskać więcej informacji, zobacz Parametryzacje typów.

Uwaga

Takie implementacje parametryzowane typu nie mogą używać żadnych konstrukcji językowych, które opierają się na określonych właściwościach argumentów typu; Obecnie nie ma możliwości wyrażenia ograniczeń typu w Q#programie ani definiowania wyspecjalizowanych implementacji dla argumentów określonego typu.

Obiekty wywołujące i funktory

Q# umożliwia wyspecjalizowane implementacje do określonych celów; Na przykład operacje w programie Q# mogą niejawnie lub jawnie definiować obsługę niektórych funktorów i wraz z nim wyspecjalizowane implementacje do wywoływania, gdy określony funktor jest stosowany do tego elementu wywołującego.

Functor, w pewnym sensie, jest fabryką, która definiuje nową implementację wywoływaną, która ma konkretny związek z wywoływanym, do którego została zastosowana. Funktory są bardziej niż tradycyjne funkcje wyższego poziomu, które wymagają dostępu do szczegółów implementacji wywoływanych, do których zostały zastosowane. W tym sensie są one podobne do innych fabryk, takich jak szablony. Można je również zastosować do wywołań sparametryzowanych typu.

Rozważmy następującą operację: ApplyQFT

    operation ApplyQFT(qs : Qubit[]) : Unit is Adj + Ctl {
        let length = Length(qs);
        Fact(length >= 1, "ApplyQFT: Length(qs) must be at least 1.");
        for i in length - 1..-1..0 {
            H(qs[i]);
            for j in 0..i - 1 {
                Controlled R1Frac([qs[i]], (1, j + 1, qs[i - j - 1]));
            }
        }
    }

Ta operacja przyjmuje argument typu Qubit[] i zwraca wartość typu Unit. Adnotacja is Adj + Ctl w deklaracji ApplyQFT elementu wskazuje, że operacja obsługuje zarówno funkcję , Adjoint jak i Controlled functor. (Aby uzyskać więcej informacji, zobacz Charakterystyka operacji). Wyrażenie Adjoint ApplyQFT uzyskuje dostęp do specjalizacji implementujące przylegające ApplyQFTdo elementu i Controlled ApplyQFT uzyskuje dostęp do specjalizacji, która implementuje kontrolowaną wersję programu ApplyQFT. Oprócz argumentu oryginalnej operacji kontrolowana wersja operacji przyjmuje tablicę kubitów sterujących i stosuje oryginalną operację pod warunkiem, że wszystkie te kubity sterujące znajdują się w stanie |1⟩.

Teoretycznie operacja, dla której można zdefiniować wersję sąsiada, powinna mieć również kontrolowaną wersję i odwrotnie. Jednak w praktyce może być trudno opracować implementację dla jednej lub drugiej, szczególnie w przypadku implementacji probabilistycznej po wzorzec powtarzania do powodzenia. Z tego powodu Q# umożliwia deklarowanie obsługi poszczególnych functorów osobno. Jednak ponieważ dwa funktory dojeżdżają do pracy, operacja definiująca obsługę obu elementów musi mieć również implementację (zwykle niejawnie zdefiniowaną, czyli wygenerowaną przez kompilator), dla których oba funktory są stosowane do operacji.

Nie ma funktorów, które można zastosować do funkcji. Funkcje mają obecnie dokładnie jedną implementację treści i nie mają dalszych specjalizacji. Na przykład deklaracja

    function Hello (name : String) : String {
        $"Hello, {name}!"
    }

jest równoważny

    function Hello (name : String) : String {
        body ... {
            $"Hello, {name}!"
        }
    }

W tym miejscu określa, body że dana implementacja ma zastosowanie do domyślnej treści funkcji Hello, co oznacza, że implementacja jest wywoływana, gdy przed wywołaniem nie zastosowano żadnych funktorów ani innych mechanizmów fabrycznych. Trzy kropki w body ... dyrektywie kompilatora wskazujące, że elementy argumentu w deklaracji funkcji powinny być kopiowane i wklejane do tego miejsca.

Przyczyny jawnego wskazania, gdzie argumenty nadrzędnej deklaracji wywoływanej mają być kopiowane i wklejane są dwa razy: jeden, nie jest konieczne powtarzanie deklaracji argumentu, a dwa, zapewnia, że funktory, które wymagają dodatkowych argumentów, takich jak Controlled functor, mogą być wprowadzane w spójny sposób.

Jeśli istnieje dokładnie jedna specjalizacja definiująca implementację treści domyślnej, dodatkowe zawijanie formularza body ... { <implementation> } może zostać pominięte.

Rekursja

Q# wywołania mogą być bezpośrednio lub pośrednio rekursywne i mogą być deklarowane w dowolnej kolejności; operacja lub funkcja może wywołać się samodzielnie lub może wywołać inną wywołaną funkcję, która bezpośrednio lub pośrednio wywołuje obiekt wywołujący.

W przypadku uruchamiania na sprzęcie kwantowym przestrzeń stosu może być ograniczona i rekursje, które przekraczają ten limit przestrzeni stosu, powodują błąd w czasie wykonywania.