你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

类型参数化

Q# 支持类型参数化作和函数。 Q# 标准库大量使用类型参数化可调用项来提供大量有用的抽象,包括功能语言熟悉的 MappedFold 等函数。

若要激发类型参数化的概念,请考虑函数 Mapped的示例,该示例将给定函数应用于数组中的每个值,并返回具有计算值的新数组。 无需指定输入和输出数组的项类型,即可完美描述此功能。 由于确切类型不会更改函数 Mapped的实现,因此应该可以为任意项类型定义此实现:我们希望定义一个 工厂模板,给定输入和输出数组中项的具体类型,返回相应的函数实现。 此概念以类型参数的形式正式化。

具体化

任何作或函数声明都可以指定一个或多个类型参数,这些参数可用作可调用对象的输入或输出的类型或部分类型或两者。 例外情况是入口点,这些入口点必须是具体点,不能进行类型参数化。 类型参数名称以刻度 (') 开头,在输入和输出类型中可能多次出现。 与可调用签名中的同一类型参数对应的所有参数都必须是同一类型。

需要对类型参数化可调用方进行具体化,也就是说,必须向其提供必要的类型参数,然后才能将其分配或作为参数传递,以便所有类型参数都可以替换为具体类型。 如果类型是内置类型之一、struct 类型或当前范围内的具体类型,则类型被视为具体类型。 以下示例演示了类型在当前范围内的具体含义,并在下面更详细地解释:

    function Mapped<'T1, 'T2> (
        mapper : 'T1 -> 'T2,
        array : 'T1[]
    ) : 'T2[] {

        mutable mapped = new 'T2[Length(array)];
        for (i in IndexRange(array)) {
            mapped w/= i <- mapper(array[i]);
        }
        return mapped;
    }

    function AllCControlled<'T3> (
        ops : ('T3 => Unit)[]
    ) : ((Bool,'T3) => Unit)[] {

        return Mapped(CControlled<'T3>, ops); 
    }

函数 CControlledMicrosoft.Quantum.Canon 命名空间中定义。 它采用类型为 'TIn => Unit 的作 op 作为参数,并返回应用原始作 (Bool, 'TIn) => Unit 类型的新作,前提是经典位(类型为 Bool) 设置为 true;这通常称为 op的经典控制版本。

函数 Mapped 采用任意项类型的数组 'T1 作为参数,将给定 mapper 函数应用于每个项,并返回包含映射项 'T2[] 类型的新数组。 它在 Microsoft.Quantum.Array 命名空间中定义。 对于本示例,对类型参数进行编号,以避免通过为两个函数中的类型参数提供同名来使讨论更加混乱。 这不是必需的;不同可调用对象的类型参数可能具有相同的名称,并且所选名称仅在该可调用对象的定义中可见且相关。

函数 AllCControlled 采用作数组,并返回一个新数组,其中包含这些作的经典控制版本。 Mapped 的调用将其类型参数 'T1 解析为 'T3 => Unit,并将其类型参数 'T2 解析为 (Bool,'T3) => Unit。 解析类型参数由编译器根据给定参数的类型推断。 我们说它们 由调用表达式的参数定义的隐式。 也可以显式指定类型参数,就像在同一行中的 CControlled 一样。 无法推断类型参数时,必须进行显式分词 CControlled<'T3>

类型 'T3AllCControlled上下文中是具体的,因为它以 AllCControlled的每个 调用 而闻名。 这意味着,只要程序入口点(不能进行类型参数化)就已知,因此每个调用 AllCControlled的具体类型 'T3,以便可以生成适合该特定类型解析的实现。 已知程序入口点后,可以在编译时消除类型参数的所有用法。 我们将此过程称为 单态化

需要一些限制,以确保这确实可以在编译时完成,而不是仅在运行时完成。

限制

请考虑以下示例:

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

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

忽略调用 Foo 会导致无限循环,它用于说明目的。 Foo 使用已传入的原始作 op 经典控制版本以及包含随机经典位的元组以及原始自变量之外调用自身。

对于递归中的每个迭代,下一次调用的类型参数 'TArg 解析为 (Bool, 'TArg),其中 'TArg 是当前调用的类型参数。 具体地说,假设使用作 H 和类型 Qubit的参数 arg 调用 Foo。 然后,Foo 使用类型参数 (Bool, Qubit)调用自身,然后使用类型参数 (Bool, (Bool, Qubit))调用 Foo,依此类推。 显然,在这种情况下,Foo 无法在编译时进行单态化。

其他限制适用于仅涉及类型参数化可调用项的调用图中的周期。 在遍历周期后,需要使用同一组类型参数调用每个可调用者。

注释

可以限制更少,并且要求在循环中每个可调用的周期中,有一定数量的周期,之后会使用原始类型参数集(例如以下函数的情况)调用它:

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

为简单起见,强制实施限制性更高的要求。 请注意,对于涉及至少一个不带任何类型参数的具体可调用对象的周期,此类可调用者可确保该循环中的类型参数始终使用固定的类型参数集调用类型参数。