Вызываемые объявления
Вызываемые объявления или вызываемые объекты, объявленные в глобальном область, по умолчанию являются общедоступными. То есть их можно использовать в любом месте в том же проекте и в проекте, который ссылается на сборку, в которой они объявлены. Модификаторы доступа позволяют ограничить видимость только текущей сборкой, что позволяет впоследствии изменять реализацию без нарушения работы кода, использующего определенную библиотеку.
В Q# поддерживаются два вида вызываемых объектов: операции и функции. Различия между ними подробно разбираются в статье Операции и функции. В Q# также поддерживается определение шаблонов, например реализаций определенного вызываемого объекта с параметризацией типов. Дополнительные сведения см. в статье Параметризация типов.
Примечание
В реализациях с параметризацией типов нельзя использовать языковые конструкции, зависящие от определенных свойств аргументов типов. В настоящее время в Q# нет возможности выражать ограничения типов или определять специализированные реализации для определенных аргументов типов.
Вызываемые объекты и функторы
В Q# допускаются специализированные реализации для определенных целей. Например, операции в Q# позволяют неявно или явно определять поддержку некоторых функторов, а также специализированные реализации, вызываемые при применении определенного функтора к данному вызываемому объекту.
Функтор — это своего рода фабрика, которая определяет новую реализацию вызываемого объекта, как-то связанную с вызываемым объектом, к которому был применен функтор. Функторы — отличаются от традиционных более высокоуровневых функций тем, что им требуется доступ к особенностям реализации вызываемого объекта, к которому они применяются. В этом смысле они похожи на другие фабрики, например шаблоны. Они также могут применяться к вызываемым объектным объектам с параметризацией типа.
Рассмотрим следующую операцию 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]));
}
}
}
Эта операция принимает аргумент типа Qubit[]
и возвращает значение типа Unit
. Аннотация is Adj + Ctl
в объявлении ApplyQFT
указывает, что операция поддерживает функторы Adjoint
и Controlled
. (Дополнительные сведения см. в разделе Характеристики операции). Выражение Adjoint ApplyQFT
обращается к специализации, реализующей присоединение ApplyQFT
, и Controlled ApplyQFT
обращается к специализации, реализующей управляемую версию ApplyQFT
.
В дополнение к аргументу исходной операции управляемая версия операции принимает массив управляющих кубитов и применяет исходную операцию при условии, что все эти управляющие кубиты находятся в состоянии |1⟩.
Теоретически операция, для которой может быть определена сопряженная версия, также должна иметь контролируемую версию и наоборот. Однако на практике может быть трудно разработать реализацию для одной из этих версий, особенно в случае с вероятностными реализациями по шаблону repeat-until-success. По этой причине Q# позволяет объявлять поддержку каждого функтора по отдельности. Однако, поскольку два функтора являются коммутируемыми, операция, в которой объявлена поддержка их обоих, также должна иметь реализацию (обычно определенную неявно, то есть создаваемую компилятором) для случаев, когда к операции применяются оба функтора.
К функциям не применяются функторы. В настоящее время функции имеют ровно одну реализацию текста и не имеют дополнительных специализаций. Например, объявление
function Hello (name : String) : String {
$"Hello, {name}!"
}
эквивалентно
function Hello (name : String) : String {
body ... {
$"Hello, {name}!"
}
}
Здесь body
указывает, что данная реализация применяется к телу функции Hello
по умолчанию, то есть реализация вызывается, если перед вызовом не были применены функторы или другие механизмы фабрики. Многоточие в body ...
соответствует директиве компилятора, указывающей, что элементы аргументов в объявлении функции должны быть скопированы и вставлены в этом месте.
Причины явного указания того, где нужно скопировать и вставить аргументы родительского вызываемого объявления, являются двоякими: одна из них не требует повторения объявления аргументов, а вторая — гарантирует, что функторы, требующие дополнительных аргументов, например Controlled
functor, могут быть введены согласованно.
Если существует только одна специализация, определяющая реализацию текста по умолчанию, дополнительная оболочка формы body ... { <implementation> }
может быть опущена.
Рекурсия
Вызываемые объекты в Q# могут быть прямо или косвенно рекурсивными и могут объявляться в любом порядке. Операция или функция может вызывать саму себя или другой вызываемый объект, который напрямую или косвенно вызывает вызывающий объект.
При выполнении рекурсии на квантовом оборудовании пространство стека может быть ограничено, и его исчерпание приведет к ошибке времени выполнения.