通用代码,也叫通用编程,是一种计算机编程风格,其中算法是根据待指定的类型编写的,然后在需要时作为参数提供的特定类型进行实例化。 这种方法由ML于1973年开创,允许编写仅在使用时操作的类型集不同的共同功能或类型,从而减少重复。 这些软件实体在Ada,C#, Delphi,Eiffel,F#,Java,Rust,Swift,TypeScript和Visual Basic .NET中称为泛型。 它们被称为ML,Scala,Haskell中的参数多态性(Haskell社区也使用术语“泛型”用于相关但有些不同的概念)和Julia; C ++和D中的模板; 有影响力的1994年着作“设计模式”中的参数化类型。设计模式的作者注意到这种技术,特别是与委托相结合时,非常强大,但是,
通用性至少在20世纪70年代,通用设施已经存在于ML,CLU和Ada等语言中,并且随后被许多基于对象和面向对象的语言所采用,包括BETA,C ++,D,Eiffel,Java,和DEC已经不复存在的Trellis-Owl语言。
在各种编程语言中以不同方式实现和支持通用性;术语“通用”在各种编程环境中也被不同地使用。例如,在Forth中,编译器可以在编译时执行代码,并且可以动态地为这些单词创建新的编译器关键字和新实现。它几乎没有暴露编译器行为的单词,因此自然地提供了通用性能力,然而,在大多数Forth文本中并未提及这些能力。类似地,动态类型语言,特别是解释型语言,默认情况下通常提供通用性,因为向函数和值赋值传递值都是类型无关紧要的,并且这种行为通常用于抽象或代码简洁,但是这通常不是标记为通用性,因为它是语言所采用的动态类型系统的直接后果。该术语已用于函数式编程,特别是在类似Haskell的语言中,它使用结构类型系统,其中类型总是参数化的,并且这些类型上的实际代码是通用的。这些用法仍然用于代码保存和抽象呈现的类似目的。
可以将数组和结构视为预定义的泛型类型。数组或结构类型的每次使用都会实例化一个新的具体类型,或者重用先前实例化的类型。数组元素类型和结构元素类型是参数化类型,用于实例化相应的泛型类型。所有这些通常都内置在编译器中,语法与其他通用结构不同。一些可扩展的编程语言尝试统一内置和用户定义的泛型类型。
面向对象的代码在使用静态类型语言创建容器类时,为每个包含的数据类型编写特定实现是不方便的,特别是如果每个数据类型的代码实际上是相同的。 例如,在C ++中,可以通过定义类模板来规避这种代码重复:
templateclass List{ /* class contents */};List list_of_animals;List list_of_cars;在上面,T是占位符,用于创建列表时指定的任何类型。 这些“tank-of-type-T”(通常称为模板)允许使用不同的数据类型重用类,只要保留某些合同(如子类型和签名)即可。 这种通用性机制不应与包含多态性相混淆,包含多态性是可交换子类的算法用法:例如,包含类型为Animal和Car的对象的Moving_Object类型的对象列表。 模板也可用于与类型无关的功能,如下面的Swap示例所示:
templatevoid Swap(T & a, T & b) //"&" passes parameters by reference{ T temp = b; b = a; a = temp;}string hello = "World!", world = "Hello, ";Swap( world, hello );cout fl x ++ fl y () -> \x -> [] Par -> \x -> [x] Rec -> \x -> x d@g -> concat . flatten . pmap fl Con t -> \x -> [] cata :: Regular d => (FunctorOf d a b -> b) -> d a -> b通用HaskellGeneric Haskell是荷兰乌得勒支大学开发的Haskell的另一个扩展。它提供的扩展是:
类型索引值被定义为在各种Haskell类型构造函数(单元,基元类型,总和,产品和用户定义的类型构造函数)上索引的值。此外,我们还可以使用构造函数案例为特定构造函数指定类型索引值的行为,并使用默认情况在另一个中重用一个泛型定义。
例如,Generic Haskell中的相等函数:
type Eq {[ * ]} t1 t2 = t1 -> t2 -> Bool type Eq {[ k -> l ]} t1 t2 = forall u1 u2. Eq {[ k ]} u1 u2 -> Eq {[ l ]} (t1 u1) (t2 u2) eq {| t :: k |} :: Eq {[ k ]} t t eq {| Unit |} _ _ = True eq {| :+: |} eqA eqB (Inl a1) (Inl a2) = eqA a1 a2 eq {| :+: |} eqA eqB (Inr b1) (Inr b2) = eqB b1 b2 eq {| :+: |} eqA eqB _ _ = False eq {| :*: |} eqA eqB (a1 :*: b1) (a2 :*: b2) = eqA a1 a2 && eqB b1 b2 eq {| Int |} = (==) eq {| Char |} = (==) eq {| Bool |} = (==)其他通用代码ML系列编程语言通过参数多态和支持仿函数的通用模块支持泛型编程。 标准ML和OCaml都提供了类似于类模板和Ada通用包的仿函数。 Scheme语法抽象也与泛型有关 - 这些实际上是模板化C ++的超集。
Verilog模块可以采用一个或多个参数,在模块实例化时将其实际值分配给该参数。 一个示例是通用寄存器阵列,其中阵列宽度通过参数给出。 这种阵列与通用线矢量相结合,可以使单个模块实现中的任意位宽的通用缓冲器或存储器模块成为可能。
VHDL源自Ada,也具有通用能力。
本词条内容贡献者为:
李嘉骞 - 博士 - 同济大学