语句和其他构造
注释
注释以两个正斜杠开始//
,并一直持续到行尾。 虽然表达式可以分成多行,但不支持表达式中间的行尾注释。 评论可能出现在Q#源文件的任何地方,包括语句无效的地方。
文档评论
以三个正斜杠开头的注释///
在编译器出现在操作,函数或类型定义之前时会特别处理。 在这种情况下,它们的内容将作为定义的可调用或用户定义类型的文档,与其他.NET语言一样。
在///
注释中,作为API文档一部分出现的文本被格式化为Markdown ,文档的不同部分由专门命名的标题表示。 作为Markdown的扩展,可以使用@"<ref target>"
包含Q#中操作,函数和用户定义类型的交叉引用,其中<ref target>
被替换为代码对象的全限定名引用。 可选地,文档引擎也可以支持额外的Markdown扩展。
例如:
/// # Summary
/// Given an operation and a target for that operation,
/// applies the given operation twice.
///
/// # Input
/// ## op
/// The operation to be applied.
/// ## target
/// The target to which the operation is to be applied.
///
/// # Type Parameters
/// ## 'T
/// The type expected by the given operation as its input.
///
/// # Example
/// ```Q#
/// // Should be equivalent to the identity.
/// ApplyTwice(H, qubit);
/// ```
///
/// # See Also
/// - Microsoft.Quantum.Primitive.H
operation ApplyTwice<'T>(op : ('T => ()), target : 'T) : () {
body {
op(target);
op(target);
}
}
以下名称被识别为文档评论标题。
- 摘要 :函数或操作的行为或类型目的的简短摘要。
- 输入 :操作或功能的输入元组的描述。 可能包含额外的Markdown小节,指示输入元组的每个单独元素。
- 输出 :操作或函数返回的元组的描述。
- 类型参数 :一个空白部分,每个泛型类型参数包含一个附加子部分。
- 示例 :正在使用的操作,功能或类型的简短示例。
- 备注 :描述操作,功能或类型某些方面的杂项散文。
- 另请参阅 :指示相关函数,操作或用户定义类型的完全限定名称的列表。
- 参考文献 :所记录项目的参考文献和引文清单。
命名空间
每个Q#操作,函数和用户定义类型都是在名称空间内定义的。 Q#遵循与其他.NET语言相同的命名规则。 但是,Q#不支持嵌套命名空间。 特别是,对于两个已定义的名称空间NS.Name1
和NS.Name2
,只能打开完全限定的名称空间 - 一个open NS;
的指令open NS;
无效 。
每个Q#文件都必须至少包含一个namespace
指令。 这包括namespace
关键字,后跟命名空间名称,开头{
,结构定义和结束}
。 所有用户定义的类型,函数和操作都必须出现在名称空间块内; 只有评论可能出现在命名空间块之外。
在名称空间块中,可以使用open
指令来允许从另一个名称空间缩写引用构造。 这包括open
关键字,后跟要打开的名称空间和终止分号。 open
指令必须出现在名称空间块中的任何function
, operation
或newtype
指令之前。 open
指令适用于整个名称空间块。
在当前名称空间中未打开的另一个名称空间中定义的构造的引用必须是其完全限定名称。 例如,在XY
名称空间中名为Op
的操作必须由其完全限定名称XYOp
,除非XY
名称空间在当前块中较早打开。
通常最好包含一个带有open
指令的名称空间。 如果两个名称空间定义具有相同名称的结构,并且当前源使用两者的结构,则需要使用完全限定名称。
格式化
大多数Q#语句和指令以一个终止分号结尾;
。 以语句块结尾的语句和指令(如for
和operation
不需要终止分号。 每个语句描述都注明是否需要终止分号。
报表可能会分成多行。 语句之间必须有一个行结束符,所以在一行中不可能有多个语句。 指令和声明(如操作声明)也可以跨越多行。
语句块
Q#语句被分组到语句块中。 一个语句块以一个开头{
开头,并以一个结束}
结束。
在词汇上被封闭在另一个块中的语句块被认为是包含块的一个子块; 包含和子块也被称为外部和内部块。
符号绑定和分配
Q#区分可变和不可变符号。 通常,鼓励使用不可变符号,因为它允许编译器执行更多优化。
对于数组,可变性或不变性同时适用于整个数组和数组元素。 也就是说,不可变数组的元素可能不会改变。
无论绑定到实例的符号的可变性如何,基于元组的元组实例或用户定义类型的内容都可能无法更改。 如果一个可变符号绑定到一个元组,那么这个符号可能被绑定到一个新的元组,但是现有的元组可能不会被修改。
操作或函数的参数被视为不可变的。 这意味着Q#不支持“out”变量。 此外,传递给操作或函数的数组元素可能不会更改。
不可变的符号
使用let
语句绑定不可变符号。 这大致相当于C#等语言中的变量声明和初始化,除了Q#符号一旦被绑定,可能不会被改变; let
绑定是不可变的。
一个简单的绑定语句由关键字let
,后跟一个标识符,一个等号=
,一个绑定标识符的表达式和一个终止分号组成。 标识符的类型被定义为与它所绑定的表达式的类型相同。
例如,声明
let i = 5;
将符号i
作为Int
绑定值5
。
如果绑定的右侧是元组或用户定义的类型,则可以使用扩展语法来解构元组:
let (i, f) = (5, 0.1);
这条语句将绑定5
到i
和0.1
到f
。
解构绑定也可以用于更复杂的元组,例如:
let (a, (b, c)) = (1, (2, 3))
let (x, y) = (1, (2, 3))
在这些例子中, a
和x
都被绑定到1
, b
被绑定到2
, c
到3
, y
被绑定到(2, 3)
。
当=的右边是一个元组值时,也可以使用元组解构:
let (r1, r2) = MeasureTwice(q1, PauliX, q2, PauliY);
可变符号
可变符号使用mutable
语句进行定义和初始化。 该语句定义了一个新的符号绑定,并指定了该绑定值可能会在代码中稍后进行更改。
可变绑定语句由关键字mutable
,后跟一个标识符,一个等号=
,一个将该标识符绑定到的表达式以及一个终止分号组成。 标识符的类型被定义为与它所绑定的表达式的类型相同。
例如,声明
mutable counter = 0;
将符号counter
定义为具有初始值0
的可变Int
。
mutable
语句不支持元组解构。
如果可变符号绑定到不可变数组,则会创建一个数组的副本并将其绑定到该符号。 修改可变数组的元素不会改变原始的不可变数组的内容。
更新可变符号
通过使用set
语句将符号绑定到新值,可以更改绑定到可变符号的值。
一个可变的重新绑定语句由关键字set
,后跟一个标识符,一个等号=
,一个重新绑定标识符的表达式和一个终止分号组成。 新值必须与原始类型兼容,并将升级为原始类型。
例如,声明
set counter = counter + 1;
从mutable
示例中增加counter
符号。
set
语句也用于设置可变数组中项目的值:
set result[1] = One;
将result
数组的第二个元素设置为One
。 请注意, result
数组必须已定义在mutable
语句中才能生效。
绑定范围
一般情况下,符号绑定超出了范围,并且在它们出现的语句块的末尾变得不起作用。该规则有两个例外:
-
for
循环的循环变量的绑定适用于for循环的主体,但不在循环结束之后。 -
repeat
/until
循环(正文,测试和修复)的所有三个部分都被视为单个作用域,因此正文中绑定的符号在测试和修正中可用。 对于这两种类型的循环,每次通过循环都会在其自己的作用域中执行,因此以后的通道中的绑定在以后的通道中不可用。
来自外部块的符号绑定由内部块继承。 每个块只能绑定一个符号; 绑定已绑定的符号是非法的。 因此,以下序列将是合法的:
if a == b {
...
let n = 5;
... // n is 5
}
let n = 8;
... // n is 8
和
if a == b {
...
let n = 5;
... // n is 5
} else {
...
let n = 8;
... // n is 8
}
但这是非法的:
let n = 5;
... // n is 5
let n = 8; // Error!!
...
如下所示:
let n = 8;
if a == b {
... // n is 8
let n = 5; // Error!
...
}
...
控制流
for循环
for
语句支持通过简单整数范围进行迭代。 该语句由关键字for
,后跟标识符,关键字in
, Range
表达式和语句块组成。 语句块(循环体)被重复执行,标识符(循环变量)绑定到范围表达式中的每个值。 请注意,如果范围表达式计算为空范围,则根本不会执行主体。
在进入循环之前,范围是完全评估的,并且在循环执行时不会改变。
例如,
for (index in 0 .. n-2) {
set results[index] = Measure([PauliX], [qubits[index]]);
}
循环变量绑定在循环体的每个入口处,并且在主体的末尾解除绑定。 特别是,循环变量在for循环完成后未被绑定。
Repeat-Until-Success循环
repeat
声明支持量子“重复直到成功”模式。 它由关键字repeat
,后面跟着语句块( 循环体),关键字until
,布尔表达式,关键字fixup
和另一个语句块( fixup )组成。 循环体,条件和修正都被认为是一个单一的作用域,所以绑定在主体中的符号在条件和修正中是可用的。
请注意,修复块是必需的,即使没有修复工作。 在这种情况下,修正应该是一个单一的表达式语句, ()
。
循环体被执行,然后评估条件。 如果条件成立,则说明已完成; 否则,将执行修正,并且从循环体开始重新执行语句。 请注意,完成fixup的执行将结束语句的范围,以便在后续重复中不会在正文或修正过程中进行符号绑定。
例如,下面的代码是一个概率电路,它使用Hadamard和T门实现重要的旋转门V3=( boldone+2iZ)/ sqrt5
。 循环平均以8/5次重复结束。 请参阅Repeat-Until-Success:单量子幺半群的非确定性分解 (Paetznick和Svore,2014)以了解详细信息。
using ancilla = Qubit[1] {
repeat {
let anc = ancilla[0];
H(anc);
T(anc);
CNOT(target,anc);
H(anc);
(Adjoint T)(anc);
H(anc);
T(anc);
H(anc);
CNOT(target,anc);
T(anc);
Z(target);
H(anc);
let result = M([anc],[PauliZ]);
} until result == Zero
fixup {
();
}
}
条件声明
if语句支持条件执行。 它由关键字if
,后跟布尔表达式和语句块( then块)组成。 这后面可以跟随任意数量的else-if子句,每个子句由关键字elif
,后跟布尔表达式和语句块( else-if块)组成。 最后,该语句可以有选择地使用else子句完成,其中else子句由另一个语句块( else块)组成。 评估条件,如果为真,则执行该块。 如果条件为假,则评估第一个else if条件; 如果它是真的,那么 - 如果块被执行。 否则,测试第二个else-if块,然后测试第三个,依此类推,直到遇到具有真实条件的子句或没有其他else-if子句。 如果原始if条件和所有else-if子句的计算结果为false,则会执行else块(如果提供了其中一个)。
请注意,无论哪个块被执行都会在其自己的范围内执行。 在if,else或if块内部进行的绑定在if语句结束后不可见。
例如,
if (result == One) {
X(target);
} else {
Z(target);
}
要么
if (i == 1) {
X(target);
} elif (i == 2) {
Y(target);
} else {
Z(target);
}
返回
return语句结束一个操作或函数的执行,并向调用者返回一个值。 它由关键字return
,后跟一个适当类型的表达式和一个终止分号组成。
可返回空元组()
可调用对象不需要返回语句。 如果需要提前退出,则可以使用return ()
。 返回任何其他类型的可调参数需要最终的返回语句。
操作中没有最大数量的返回语句。 如果语句遵循块内的返回语句,编译器可能会发出警告。
例如,
return 1;
要么
return ();
要么
return (results, qubits);
失败
失败语句结束一个操作的执行并向调用者返回一个错误值。 它由关键字fail
,后跟一个字符串和一个终止分号。 该字符串作为错误消息返回到经典驱动程序。
对操作中失败语句的数量没有限制。 如果语句在块内遵循失败语句,编译器可能会发出警告。
例如,
fail $"Impossible state reached";
要么
fail $"Syndrome {syn} is incorrect";
Qubit管理
请注意,这些语句在函数体内都不允许。
清理Qubits
using语句用于获取在语句块中使用的新量子位。 量子位保证被初始化为计算Zero
状态。 量子位应该在语句块的末尾处于计算的Zero
状态; 鼓励模拟器执行此操作。
该语句由关键字using
,其后跟随应该绑定到生成的量子Qubit
组的符号=
,要获取的类型( Qubit
), [
, Int
表达式]
以及量子位将在其中生成的语句块能得到的。
例如,
using (qubits = Qubit[bits * 2 + 3]) {
...
}
肮脏的Qubits
借用语句用于在语句块中分配临时使用的量子位。 借款人承诺离开量子位时的状态与他们借用时的状态相同。 这种量子位通常被称为“脏ancilla”。 请参见使用基于Toffoli模块化乘法的2n + 2量子比特分解(Haner,Roetteler和Svore 2017),以查看脏ancilla使用的示例。
在借用量子比特时,系统将首先尝试填充正在使用的但在borrowing
说明正文中未被访问的量子比特的请求。 如果没有足够的这种量子位,那么它将分配新的量子位来完成请求。
该语句由关键字borrowing
,其后跟随应该与所得到的量子Qubit
=
,要获取的类型( Qubit
), [
,一个Int
表达式]
绑定的符号,以及其中量子位将能得到的。
例如,
borrowing qubits = Qubit[bits * 2 + 3] {
...
}
表达评估陈述
类型()
任何有效Q#表达式都可以作为语句来评估。 这在调用对return ()
量子位的操作时主要用到,因为该语句的目的是修改隐式量子态。 表达式评估语句需要终止分号。
例如,
X(q);
要么
CNOT(control, target);
要么
(Adjoint T)(q1);