De Wikipedia, la enciclopedia libre
Saltar a navegación Saltar a búsqueda

La programación genérica es un estilo de programación de computadoras en el que los algoritmos se escriben en términos de tipos que se especificarán más adelante y que luego se instancian cuando se necesitan para tipos específicos proporcionados como parámetros . Este enfoque, iniciado por el lenguaje de programación ML en 1973, [1] [2] permite escribir funciones o tipos comunes que difieren solo en el conjunto de tipos en los que operan cuando se usan, reduciendo así la duplicación . Dichas entidades de software se conocen como genéricos en Ada , C #, Delphi , Eiffel , F # , Java , Nim , Python , Rust , Swift , TypeScript y Visual Basic .NET . Se conocen como polimorfismo paramétrico en ML , Scala , Julia y Haskell (la comunidad Haskell también usa el término "genérico" para un concepto relacionado pero algo diferente); plantillas en C ++ y D ; y tipos parametrizados en el influyente libro de 1994Patrones de diseño . [3]

El término "programación genérica" ​​fue acuñado originalmente por David Musser y Alexander Stepanov [4] en un sentido más específico que el anterior, para describir un paradigma de programación mediante el cual los requisitos fundamentales sobre tipos se abstraen de ejemplos concretos de algoritmos y estructuras de datos y se formalizan como conceptos , con funciones genéricas implementadas en términos de estos conceptos, típicamente usando mecanismos de genéricoidad del lenguaje como se describe arriba.

Stepanov-Musser y otros paradigmas de programación genéricos [ editar ]

La programación genérica se define en Musser y Stepanov (1989) de la siguiente manera,

La programación genérica se centra en la idea de abstraerse de algoritmos concretos y eficientes para obtener algoritmos genéricos que se pueden combinar con diferentes representaciones de datos para producir una amplia variedad de software útil.

-  Musser, David R .; Stepanov, Alexander A., ​​Programación genérica [5]

El paradigma de "programación genérica" ​​es un enfoque de la descomposición de software mediante el cual los requisitos fundamentales sobre tipos se abstraen de ejemplos concretos de algoritmos y estructuras de datos y se formalizan como conceptos , de manera análoga a la abstracción de teorías algebraicas en álgebra abstracta . [6] Los primeros ejemplos de este enfoque de programación se implementaron en Scheme y Ada, [7] aunque el ejemplo más conocido es la Biblioteca de plantillas estándar (STL), [8] [9] que desarrolló una teoría de iteradores que se utiliza para desacoplar secuencias de estructuras de datos y los algoritmos que operan en ellas.

Por ejemplo, dadas N estructuras de datos de secuencia, por ejemplo, lista enlazada individualmente, vector, etc., y M algoritmos para operar en ellos, por ejemplo find, sortetc., un enfoque directo implementaría cada algoritmo específicamente para cada estructura de datos, dando N × M combinaciones para implementar. Sin embargo, en el enfoque de programación genérico, cada estructura de datos devuelve un modelo de un concepto de iterador (un tipo de valor simple que puede desreferenciarse para recuperar el valor actual, o cambiarse para apuntar a otro valor en la secuencia) y cada algoritmo se escribe en su lugar genéricamente con argumentos de tales iteradores, por ejemplo, un par de iteradores que apuntan al principio y al final de la subsecuencia o rangoprocesar. Por tanto, sólo es necesario implementar N + M combinaciones de estructura de datos y algoritmo. En el STL se especifican varios conceptos de iterador, cada uno de los cuales es un refinamiento de conceptos más restrictivos, por ejemplo, los iteradores de avance solo proporcionan movimiento al siguiente valor en una secuencia (por ejemplo, adecuado para una lista enlazada individualmente o un flujo de datos de entrada), mientras que un acceso aleatorio El iterador también proporciona acceso directo en tiempo constante a cualquier elemento de la secuencia (por ejemplo, adecuado para un vector). Un punto importante es que una estructura de datos devolverá un modelo del concepto más general que se puede implementar de manera eficiente: complejidad computacional.Los requisitos son explícitamente parte de la definición del concepto. Esto limita las estructuras de datos a las que se puede aplicar un algoritmo dado y estos requisitos de complejidad son un factor determinante de la elección de la estructura de datos. La programación genérica se ha aplicado de manera similar en otros dominios, por ejemplo, algoritmos de gráficos. [10]

Tenga en cuenta que aunque este enfoque a menudo utiliza características de lenguaje de genéricos / plantillas en tiempo de compilación , de hecho es independiente de los detalles técnicos del lenguaje en particular. El pionero de la programación genérica Alexander Stepanov escribió:

La programación genérica consiste en abstraer y clasificar algoritmos y estructuras de datos. Se inspira en Knuth y no en la teoría de tipos. Su objetivo es la construcción incremental de catálogos sistemáticos de estructuras de datos y algoritmos útiles, eficientes y abstractos. Tal empresa sigue siendo un sueño.

-  Alexander Stepanov, Breve historia de STL [11] [12]

Creo que las teorías de los iteradores son tan fundamentales para la informática como las teorías de los anillos o los espacios de Banach son fundamentales para las matemáticas.

-  Alexander Stepanov, una entrevista con A. Stepanov [13]

Bjarne Stroustrup señaló,

Siguiendo a Stepanov, podemos definir la programación genérica sin mencionar las características del lenguaje: Elevar algoritmos y estructuras de datos de ejemplos concretos a su forma más general y abstracta.

-  Bjarne Stroustrup, Evolución de un lenguaje en y para el mundo real: C ++ 1991-2006 [12]

Otros paradigmas de programación que se han descrito como programación genérica incluyen la programación genérica de tipo de datos como se describe en "Programación genérica: una introducción". [14] El enfoque Scrap your boilerplate es un enfoque de programación genérico ligero para Haskell. [15]

En este artículo distinguimos los paradigmas de programación de alto nivel de la programación genérica , arriba, de los mecanismos de genéricoidad del lenguaje de programación de nivel inferior usados ​​para implementarlos (ver Soporte de lenguaje de programación para genérico ). Para una mayor discusión y comparación de paradigmas de programación genéricos, consulte. [dieciséis]

Soporte de lenguaje de programación para genérico [ editar ]

Instalaciones Genericidad han existido en lenguajes de alto nivel por lo menos desde la década de 1970 en lenguajes como ML , CLU y Ada , y posteriormente fueron adoptados por muchos basados en objetos y orientados a objetos idiomas, incluyendo BETA , C ++ , D , Eiffel , Java , y el ahora desaparecido idioma Trellis-Owl de DEC .

Genericity se implementa y admite de manera diferente en varios lenguajes de programación; el término "genérico" también se ha utilizado de forma diferente en varios contextos de programación. Por ejemplo, en Forth el compilador puede ejecutar código mientras se compila y se pueden crear nuevas palabras clave del compilador y nuevas implementaciones para esas palabras sobre la marcha. Tiene pocas palabras que expongan el comportamiento del compilador y, por lo tanto, ofrece naturalmente capacidades de genérico que, sin embargo, no se mencionan como tales en la mayoría de los textos de Forth. De manera similar, los lenguajes tipados dinámicamente, especialmente los interpretados, generalmente ofrecen genéricode forma predeterminada, ya que tanto el paso de valores a las funciones como la asignación de valores son indiferentes al tipo y dicho comportamiento a menudo se utiliza para la abstracción o la concisión del código, sin embargo, esto no se suele etiquetar como genérico, ya que es una consecuencia directa del sistema de tipado dinámico empleado por el lenguaje. [ cita requerida ] El término se ha utilizado en programación funcional , específicamente en lenguajes similares a Haskell , que utilizan un sistema de tipos estructurales donde los tipos son siempre paramétricos y el código real de esos tipos es genérico. Estos usos todavía tienen un propósito similar de guardar código y renderizar una abstracción.

Las matrices y estructuras se pueden ver como tipos genéricos predefinidos. Cada uso de una matriz o tipo de estructura crea una instancia de un nuevo tipo concreto o reutiliza un tipo instanciado anterior. Los tipos de elementos de matriz y los tipos de elementos de estructura son tipos parametrizados, que se utilizan para instanciar el tipo genérico correspondiente. Todo esto suele estar integrado en el compilador y la sintaxis difiere de otras construcciones genéricas. Algunos lenguajes de programación extensibles intentan unificar tipos genéricos integrados y definidos por el usuario.

A continuación, se incluye un amplio estudio de los mecanismos de genéricoidad en los lenguajes de programación. Para obtener una encuesta específica que compare la idoneidad de los mecanismos para la programación genérica, consulte. [17]

En lenguajes orientados a objetos [ editar ]

Al crear clases de contenedor en lenguajes de tipo estático, es inconveniente escribir implementaciones específicas para cada tipo de datos contenido, especialmente si el código para cada tipo de datos es virtualmente idéntico. Por ejemplo, en C ++, esta duplicación de código se puede eludir definiendo una plantilla de clase:

template  < typename  T > class  List  {  // Contenido de la clase. };List < Animal >  list_of_animals ; List < Car >  list_of_cars ;

Arriba, Thay un marcador de posición para cualquier tipo que se especifique cuando se crea la lista. Estos "contenedores de tipo T", comúnmente llamados plantillas , permiten reutilizar una clase con diferentes tipos de datos siempre que se mantengan ciertos contratos como subtipos y firma . Este mecanismo de genéricoidad no debe confundirse con el polimorfismo de inclusión , que es el uso algorítmico de subclases intercambiables: por ejemplo, una lista de objetos de tipo que Moving_Objectcontiene objetos de tipo Animaly Car. Las plantillas también se pueden utilizar para funciones independientes del tipo, como en el Swapejemplo siguiente:

// "&" pasa parámetros por plantilla de  referencia < typename  T > void  Swap ( T &  a ,  T &  b )  {  T  temp  =  b ;  b  =  a ;  a  =  temp ; }std :: string  hola  =  "¡Mundo!" ; std :: string  world  =  "Hola" ; Swap ( mundo ,  hola ); std :: cout  <<  hola  <<  mundo  <<  std :: endl ;  // La salida es "¡Hola, mundo!".

El templateconstructo de C ++ utilizado anteriormente se cita ampliamente [ cita requerida ] como el constructo de genéricaidad que popularizó la noción entre los programadores y diseñadores de lenguajes y es compatible con muchos modismos de programación genéricos. El lenguaje de programación D también ofrece plantillas totalmente genéricas basadas en el precedente de C ++ pero con una sintaxis simplificada. El lenguaje de programación Java ha proporcionado recursos genéricos basados ​​sintácticamente en C ++ desde la introducción de J2SE 5.0.

C # 2.0, Oxygene 1.5 (también conocido como Chrome) y Visual Basic .NET 2005 tienen construcciones que aprovechan el soporte para genéricos presentes en Microsoft .NET Framework desde la versión 2.0.

Genéricos en Ada [ editar ]

Ada ha tenido genéricos desde que se diseñó por primera vez en 1977-1980. La biblioteca estándar utiliza genéricos para proporcionar muchos servicios. Ada 2005 agrega una completa biblioteca de contenedores genéricos a la biblioteca estándar, que se inspiró en la biblioteca de plantillas estándar de C ++ .

Una unidad genérica es un paquete o subprograma que toma uno o más parámetros formales genéricos .

Un parámetro formal genérico es un valor, una variable, una constante, un tipo, un subprograma o incluso una instancia de otra unidad genérica designada. Para los tipos formales genéricos, la sintaxis distingue entre tipos discretos, de punto flotante, de punto fijo, de acceso (puntero), etc. Algunos parámetros formales pueden tener valores predeterminados.

Para instanciar una unidad genérica, el programador pasa parámetros reales para cada formal. La instancia genérica se comporta como cualquier otra unidad. Es posible crear instancias de unidades genéricas en tiempo de ejecución , por ejemplo, dentro de un bucle.

Ejemplo [ editar ]

La especificación de un paquete genérico:

  Max_Size  genérico :  Natural ;  - un  tipo de  valor formal genérico Element_Type  es  privado ;  - un tipo formal genérico; acepta cualquier  paquete de  tipo ilimitado. Las pilas  son de  tipo  Size_Type  tienen un  rango  0  ..  Max_Size ;  type  Stack  es  privado limitado  ; procedimiento Crear ( S : out Stack ; Initial_Size : in Size_Type : = Max_Size ); procedimiento            Empuje  ( Into  : in  out  Stack ;  Element  : in  Element_Type );  procedimiento  Pop  ( From  : in  out  Stack ;  Element  : out  Element_Type );  Desbordamiento  :  excepción ;  Rebase por defecto  :  excepción ;  el  subtipo  privado Index_Type  es  Size_Type  rango  1  ..  Max_Size ;  tipo  Vector  es  matriz ( Rango de Index_Type  <>) de Element_Type ; type Stack ( Allocated_Size : Size_Type : = 0 ) es el registro Top : Index_Type ; Almacenamiento : Vector ( 1 .. Allocated_Size ); registro final ; End Stacks ;                       

Creación de una instancia del paquete genérico:

 type  Bookmark_Type  es  nuevo  Natural ;  - registra una ubicación en el documento de texto que estamos editando el paquete  Bookmark_Stacks  es nuevo  Stacks  ( Max_Size  => 20 ,  Element_Type  => Bookmark_Type );  - Permite al usuario saltar entre ubicaciones grabadas en un documento

Usando una instancia de un paquete genérico:

 type  Document_Type  es un  registro  Contenido  :  Ada . Cuerdas . Ilimitado . Unbounded_String ;  Marcadores  :  Bookmark_Stacks . Pila ;  registro final ; el procedimiento  Editar  ( Document_Name  : in  String )  es  Document  :  Document_Type ;  begin  : inicializa la pila de marcadores:  Bookmark_Stacks . Crear  ( S  =>  Documento . Marcadores ,  Tamaño_inicial  =>  10 );  - Ahora, abra el archivo Document_Name y léalo en ...  end  Edit ;
Ventajas y limitaciones [ editar ]

La sintaxis del lenguaje permite la especificación precisa de restricciones sobre parámetros formales genéricos. Por ejemplo, es posible especificar que un tipo formal genérico solo aceptará un tipo modular como real. También es posible expresar restricciones entre parámetros formales genéricos; por ejemplo:

 El  tipo  genérico Index_Type  es  (<>);  - debe ser un tipo discreto  tipo  ELEMENT_TYPE  es  privada ;  - puede ser cualquier tipo de  tipo no  limitado Array_Type  es una  matriz  ( rango de Index_Type  <>) de Element_Type ;   

En este ejemplo, Array_Type está restringido por Index_Type y Element_Type. Al crear una instancia de la unidad, el programador debe pasar un tipo de matriz real que satisfaga estas restricciones.

La desventaja de este control detallado es una sintaxis complicada, pero, debido a que todos los parámetros formales genéricos están completamente definidos en la especificación, el compilador puede crear instancias de genéricos sin mirar el cuerpo del genérico.

A diferencia de C ++, Ada no permite instancias genéricas especializadas y requiere que todos los genéricos sean instanciados explícitamente. Estas reglas tienen varias consecuencias:

  • el compilador puede implementar genéricos compartidos : el código objeto de una unidad genérica se puede compartir entre todas las instancias (a menos que el programador solicite la inclusión de subprogramas, por supuesto). Como consecuencias adicionales:
    • no hay posibilidad de que el código se hinche (el código es común en C ++ y requiere un cuidado especial, como se explica a continuación).
    • Es posible crear instancias de genéricos en tiempo de ejecución, así como en tiempo de compilación, ya que no se requiere un nuevo código de objeto para una nueva instancia.
    • los objetos reales correspondientes a un objeto formal genérico siempre se consideran no estáticos dentro del genérico; consulte Objetos formales genéricos en Wikilibro para obtener detalles y consecuencias.
  • si todas las instancias de un genérico son exactamente iguales, es más fácil revisar y comprender los programas escritos por otros; no hay "casos especiales" a tener en cuenta.
  • siendo todas las instancias explícitas, no hay instancias ocultas que puedan dificultar la comprensión del programa.
  • Ada no permite la "metaprogramación de plantillas", porque no permite especializaciones.

Plantillas en C ++ [ editar ]

C ++ usa plantillas para habilitar técnicas de programación genéricas. La biblioteca estándar de C ++ incluye la biblioteca de plantillas estándar o STL que proporciona un marco de plantillas para estructuras de datos y algoritmos comunes. Las plantillas en C ++ también se pueden usar para la metaprogramación de plantillas , que es una forma de evaluar previamente parte del código en tiempo de compilación en lugar de en tiempo de ejecución . Usando la especialización de plantillas, las plantillas de C ++ se consideran completas de Turing .

Descripción general técnica [ editar ]

Hay dos tipos de plantillas: plantillas de funciones y plantillas de clases. Una plantilla de función es un patrón para crear funciones ordinarias basadas en los tipos de parametrización suministrados cuando se crea una instancia. Por ejemplo, la biblioteca de plantillas estándar de C ++ contiene la plantilla de función max(x, y)que crea funciones que devuelven x o y, la que sea mayor. max()podría definirse así:

plantilla  < typename  T > T  max ( T  x ,  T  y )  {  return  x  <  y  ?  y  :  x ; }

Las especializaciones de esta plantilla de función, instancias con tipos específicos, se pueden llamar como una función ordinaria:

std :: cout  <<  max ( 3 ,  7 );  // Salidas 7.

El compilador examina los argumentos utilizados para llamar maxy determina que se trata de una llamada a max(int, int). A continuación, crea la instancia de una versión de la función donde el tipo de parametrización Tes int, haciendo que el equivalente de la siguiente función:

int  max ( int  x ,  int  y )  {  return  x  <  y  ?  y  :  x ; }

Esto funciona si los argumentos xy yson enteros, cadenas o cualquier otro tipo para el que la expresión x < yes sensible, o más específicamente, para cualquier tipo para el que operator<se define. La herencia común no es necesaria para el conjunto de tipos que se pueden usar, por lo que es muy similar a la tipificación de pato . Un programa que define un tipo de datos personalizado puede usar la sobrecarga del operador para definir el significado de <para ese tipo, permitiendo así su uso con la max()plantilla de función. Si bien esto puede parecer un beneficio menor en este ejemplo aislado, en el contexto de una biblioteca completa como la STL, permite al programador obtener una amplia funcionalidad para un nuevo tipo de datos, simplemente definiendo algunos operadores para él. Simplemente definiendo<permite que un tipo se use con los algoritmos estándar sort(), stable_sort()y binary_search(), o que se coloque dentro de estructuras de datos como sets, montones y matrices asociativas .

Las plantillas de C ++ son completamente seguras para los tipos en tiempo de compilación. Como demostración, el tipo estándar complexno define al <operador, porque no existe un orden estricto en los números complejos . Por lo tanto, max(x, y)fallará con un error de compilación, si x y y son complexvalores. Del mismo modo, otras plantillas que se basan en <no se pueden aplicar a los complexdatos a menos que se proporcione una comparación (en forma de un funtor o función). Por ejemplo: A complexno se puede utilizar como clave para a a mapmenos que se proporcione una comparación. Desafortunadamente, los compiladores históricamente generan mensajes de error algo esotéricos, largos e inútiles para este tipo de error. Asegurarse de que un determinado objeto se adhiera a unEl protocolo de método puede aliviar este problema. Los lenguajes que usan en comparelugar de <también pueden usar complexvalores como claves.

El segundo tipo de plantilla, una plantilla de clase, extiende el mismo concepto a las clases. Una especialización de plantilla de clase es una clase. Las plantillas de clase se utilizan a menudo para crear contenedores genéricos. Por ejemplo, el STL tiene un contenedor de lista enlazada . Para hacer una lista enlazada de enteros, se escribe list<int>. Se indica una lista de cadenas list<string>. A listtiene un conjunto de funciones estándar asociadas, que funcionan para cualquier tipo de parametrización compatible.

Especialización en plantillas [ editar ]

Una característica poderosa de las plantillas de C ++ es la especialización de plantillas . Esto permite que se proporcionen implementaciones alternativas basadas en ciertas características del tipo parametrizado que se está instanciando. La especialización de plantillas tiene dos propósitos: permitir ciertas formas de optimización y reducir el exceso de código.

Por ejemplo, considere un sort()función de plantilla. Una de las actividades principales que realiza dicha función es intercambiar o intercambiar los valores en dos de las posiciones del contenedor. Si los valores son grandes (en términos de la cantidad de bytes que se necesitan para almacenar cada uno de ellos), a menudo es más rápido construir primero una lista separada de punteros a los objetos, ordenar esos punteros y luego construir la secuencia ordenada final . Sin embargo, si los valores son bastante pequeños, generalmente es más rápido simplemente intercambiar los valores en el lugar según sea necesario. Además, si el tipo parametrizado ya es de algún tipo de puntero, entonces no hay necesidad de construir una matriz de punteros separada. La especialización de plantillas permite al creador de plantillas escribir diferentes implementaciones y especificar las características que deben tener los tipos parametrizados para cada implementación a utilizar.

A diferencia de las plantillas de funciones, las plantillas de clases pueden estar parcialmente especializadas . Eso significa que se puede proporcionar una versión alternativa del código de la plantilla de clase cuando se conocen algunos de los parámetros de la plantilla, dejando otros parámetros de la plantilla genéricos. Esto se puede usar, por ejemplo, para crear una implementación predeterminada (la especialización primaria ) que asume que copiar un tipo de parametrización es costoso y luego crear especializaciones parciales para tipos que son baratos de copiar, aumentando así la eficiencia general. Los clientes de dicha plantilla de clase simplemente usan especializaciones de la misma sin necesidad de saber si el compilador usó la especialización primaria o alguna especialización parcial en cada caso. Las plantillas de clase también pueden ser completamente especializadas, lo que significa que se puede proporcionar una implementación alternativa cuando se conocen todos los tipos de parametrización.

Ventajas y desventajas [ editar ]

Algunos usos de las plantillas, como la max()función, se completaban previamente con macros de preprocesador similares a funciones (un legado del lenguaje de programación C ). Por ejemplo, aquí hay una posible max()macro:

#define max (a, b) ((a) <(b)? (b): (a))

Las macros son expandidas por el preprocesador , antes de la compilación propiamente dicha; las plantillas se expanden en tiempo de compilación. Las macros siempre se expanden en línea; Las plantillas también se pueden expandir como funciones en línea cuando el compilador lo considere apropiado. Por tanto, tanto las macros de funciones como las plantillas de funciones no tienen sobrecarga de tiempo de ejecución.

Sin embargo, las plantillas generalmente se consideran una mejora con respecto a las macros para estos fines. Las plantillas son de tipo seguro. Las plantillas evitan algunos de los errores comunes que se encuentran en el código que hace un uso intensivo de macros similares a funciones, como evaluar parámetros con efectos secundarios dos veces. Quizás lo más importante es que las plantillas se diseñaron para ser aplicables a problemas mucho mayores que las macros.

Hay cuatro inconvenientes principales en el uso de plantillas: funciones compatibles, compatibilidad con el compilador, mensajes de error deficientes y exceso de código :

  1. Las plantillas en C ++ carecen de muchas características, lo que hace que implementarlas y usarlas de una manera sencilla a menudo sea imposible. En cambio, los programadores tienen que confiar en trucos complicados que conducen a un código inflado, difícil de entender y difícil de mantener. Los desarrollos actuales en los estándares de C ++ exacerban este problema al hacer un uso intensivo de estos trucos y crear muchas características nuevas para las plantillas en ellos o con ellos en mente.
  2. Históricamente, muchos compiladores tienen un soporte deficiente para las plantillas, por lo que el uso de plantillas puede hacer que el código sea algo menos portátil. El soporte también puede ser deficiente cuando se usa un compilador de C ++ con un enlazador que no es compatible con C ++, o cuando se intenta usar plantillas a través de los límites de la biblioteca compartida . Sin embargo, la mayoría de los compiladores modernos ahora tienen un soporte de plantilla estándar y bastante robusto, y el nuevo estándar C ++ , C ++ 11 , aborda estos problemas.
  3. Casi todos los compiladores producen mensajes de error confusos, largos o, en ocasiones, inútiles cuando se detectan errores en el código que utiliza plantillas. [18] Esto puede dificultar el desarrollo de plantillas.
  4. Finalmente, el uso de plantillas requiere que el compilador genere una instancia separada de la clase o función con plantilla para cada permutación de los parámetros de tipo usados ​​con ella. (Esto es necesario porque los tipos en C ++ no son todos del mismo tamaño, y los tamaños de los campos de datos son importantes para el funcionamiento de las clases). Por lo tanto, el uso indiscriminado de plantillas puede provocar un exceso de código , lo que resulta en ejecutables excesivamente grandes. Sin embargo, el uso prudente de la especialización y derivación de plantillas puede reducir drásticamente tal exceso de código en algunos casos:

Entonces, ¿se puede usar la derivación para reducir el problema del código replicado porque se usan plantillas? Esto implicaría derivar una plantilla de una clase ordinaria. Esta técnica resultó exitosa para frenar la hinchazón del código en el uso real. Las personas que no utilizan una técnica como esta han descubierto que el código replicado puede costar megabytes de espacio de código incluso en programas de tamaño moderado.

-  Bjarne Stroustrup , El diseño y evolución de C ++, 1994 [19]

Las instancias adicionales generadas por las plantillas también pueden hacer que los depuradores tengan dificultades para trabajar correctamente con las plantillas. Por ejemplo, establecer un punto de interrupción de depuración dentro de una plantilla desde un archivo de origen puede perder la configuración del punto de interrupción en la instanciación real deseada o puede establecer un punto de interrupción en cada lugar donde se instancia la plantilla.

Además, debido a que el compilador necesita realizar expansiones de plantillas tipo macro y generar diferentes instancias de ellas en tiempo de compilación, el código fuente de implementación para la clase o función con plantilla debe estar disponible (por ejemplo, incluido en un encabezado) para el código que lo usa. Las clases o funciones con plantilla, incluida gran parte de la biblioteca de plantillas estándar (STL), si no se incluyen en los archivos de encabezado, no se pueden compilar. (Esto contrasta con el código sin plantilla, que puede compilarse en binario, proporcionando solo un archivo de encabezado de declaraciones para el código que lo usa). Esto puede ser una desventaja al exponer el código de implementación, que elimina algunas abstracciones y podría restringir su uso en proyectos de código cerrado. [ cita requerida ]

Plantillas en D [ editar ]

El lenguaje de programación D admite plantillas basadas en diseño en C ++. La mayoría de los modismos de las plantillas de C ++ se trasladarán a D sin alteraciones, pero D agrega algunas funciones adicionales:

  • Los parámetros de plantilla en D no están restringidos solo a tipos y valores primitivos, sino que también permiten valores arbitrarios en tiempo de compilación (como cadenas y literales de estructura) y alias a identificadores arbitrarios, incluidas otras plantillas o instancias de plantillas.
  • Las restricciones de plantilla y la static ifdeclaración proporcionan una alternativa al mecanismo de falla de sustitución de C ++ no es un error (SFINAE), similar a los conceptos de C ++ .
  • La is(...)expresión permite la instanciación especulativa para verificar los rasgos de un objeto en el momento de la compilación.
  • La autopalabra clave y la typeofexpresión permiten la inferencia de tipos para declaraciones de variables y valores de retorno de funciones, lo que a su vez permite "tipos de Voldemort" (tipos que no tienen un nombre global). [20]

Las plantillas de D utilizan una sintaxis diferente que en C ++: mientras que en los parámetros de plantilla C ++ son envueltos entre paréntesis angulares ( Template<param1, param2>), D utiliza un signo de exclamación y paréntesis: Template!(param1, param2). Esto evita las dificultades de análisis de C ++ debido a la ambigüedad con los operadores de comparación. Si solo hay un parámetro, se pueden omitir los paréntesis.

Convencionalmente, D combina las características anteriores para proporcionar polimorfismo en tiempo de compilación utilizando programación genérica basada en rasgos. Por ejemplo, un rango de entrada se define como cualquier tipo que satisfaga las comprobaciones realizadas por isInputRange, que se define de la siguiente manera:

plantilla  isInputRange ( R ) {  enum  bool  isInputRange  =  es ( typeof (  ( inout  int  =  0 )  {  R  r  =  R . init ;  // puede definir un objeto de rango  si  ( r . vaciar )  {}  // puede probar para vacío  r . popFront ();  // puede invocar popFront ()  auto  h  =  r . front ; // puede obtener el frente del rango  })); }

Una función que acepta solo rangos de entrada puede usar la plantilla anterior en una restricción de plantilla:

auto  fun ( Rango ) ( Rango de  rango )  if  ( isInputRange ! Range ) {  // ... }
Generación de código [ editar ]

Además de la metaprogramación de plantillas, D también proporciona varias características para permitir la generación de código en tiempo de compilación:

  • La importexpresión permite leer un archivo del disco y usar su contenido como una expresión de cadena.
  • La reflexión en tiempo de compilación permite enumerar e inspeccionar declaraciones y sus miembros durante la compilación.
  • Los atributos definidos por el usuario permiten a los usuarios adjuntar identificadores arbitrarios a las declaraciones, que luego se pueden enumerar mediante la reflexión en tiempo de compilación.
  • La ejecución de funciones en tiempo de compilación (CTFE) permite interpretar un subconjunto de D (restringido a operaciones seguras) durante la compilación.
  • Los mixins de cadenas permiten evaluar y compilar el contenido de una expresión de cadena como código D que pasa a formar parte del programa.

La combinación de lo anterior permite generar código basado en declaraciones existentes. Por ejemplo, los marcos de serialización de D pueden enumerar los miembros de un tipo y generar funciones especializadas para cada tipo serializado para realizar la serialización y deserialización. Los atributos definidos por el usuario podrían indicar además las reglas de serialización.

La importejecución de la función de expresión y tiempo de compilación también permite implementar de manera eficiente lenguajes específicos del dominio . Por ejemplo, dada una función que toma una cadena que contiene una plantilla HTML y devuelve un código fuente D equivalente, es posible usarla de la siguiente manera:

// Importa el contenido de example.htt como una constante de manifiesto de cadena. enum  htmlTemplate  =  import ( "ejemplo.htt" );// Transpile la plantilla HTML a código D. enumeración  htmlDCode  =  htmlTemplateToD ( htmlTemplate );// Pegue el contenido de htmlDCode como código D. mixin ( htmlDCode );

Genericidad en Eiffel [ editar ]

Las clases genéricas han sido parte de Eiffel desde el método original y el diseño del lenguaje. Las publicaciones de la Fundación de Eiffel, [21] [22] utilizan el término genericity para describir la creación y uso de clases genéricas.

Genérico básico / sin restricciones [ editar ]

Las clases genéricas se declaran con su nombre de clase y una lista de uno o más parámetros genéricos formales . En el siguiente código, la clase LISTtiene un parámetro genérico formalG

clase  LISTA  [ G ]  ... característica  - Acceso  artículo :  G  - El elemento actualmente apuntado por el cursor  ... característica  - elemento de cambio de  puesto  ( NEW_ITEM :  G )  - Añadir `NEW_ITEM' al final de la lista  ...

Los parámetros genéricos formales son marcadores de posición para nombres de clases arbitrarios que se proporcionarán cuando se haga una declaración de la clase genérica, como se muestra en las dos derivaciones genéricas a continuación, donde ACCOUNTy DEPOSITson otros nombres de clase. ACCOUNTy DEPOSITse consideran parámetros genéricos reales, ya que proporcionan nombres de clases reales para sustituir Gen el uso real.

 list_of_accounts :  LIST  [ ACCOUNT ]  - Lista de cuentas list_of_deposits :  LISTA  [ DEPÓSITO ]  - Lista de Depósito

Dentro del sistema de tipos de Eiffel, aunque la clase LIST [G]se considera una clase, no se considera un tipo. Sin embargo, una derivación genérica de LIST [G]tal como LIST [ACCOUNT]se considera un tipo.

Genérico restringido [ editar ]

Para la clase de lista que se muestra arriba, un parámetro genérico real que sustituye Gpuede ser cualquier otra clase disponible. Para restringir el conjunto de clases de las que se pueden elegir parámetros genéricos reales válidos, se puede especificar una restricción genérica . En la declaración de clase a SORTED_LISTcontinuación, la restricción genérica dicta que cualquier parámetro genérico real válido será una clase que hereda de la clase COMPARABLE. La restricción genérica asegura que los elementos de a SORTED_LISTpuedan, de hecho, ser ordenados.

clase  SORTED_LIST  [ G  ->  COMPARABLE ]

Genéricos en Java [ editar ]

El soporte para los genéricos , o "contenedores de tipo-T" se agregó al lenguaje de programación Java en 2004 como parte de J2SE 5.0. En Java, los genéricos solo se verifican en el momento de la compilación para verificar la corrección de tipos. La información de tipo genérico se elimina a través de un proceso llamado borrado de tipo , para mantener la compatibilidad con implementaciones de JVM antiguas, lo que la hace no disponible en tiempo de ejecución. Por ejemplo, a List<String>se convierte al tipo sin formato List. El compilador inserta conversiones de tipos para convertir los elementos al Stringtipo cuando se recuperan de la lista, lo que reduce el rendimiento en comparación con otras implementaciones, como las plantillas de C ++.

Genericidad en .NET [C #, VB.NET] [ editar ]

Los genéricos se agregaron como parte de .NET Framework 2.0 en noviembre de 2005, sobre la base de un prototipo de investigación de Microsoft Research iniciado en 1999. [23] Aunque son similares a los genéricos en Java, los genéricos .NET no aplican borrado de tipos , pero implementan genéricos como un Mecanismo de primera clase en tiempo de ejecución mediante reificación . Esta opción de diseño proporciona una funcionalidad adicional, como permitir la reflexión con preservación de tipos genéricos, así como aliviar algunas de las limitaciones del borrado (como no poder crear matrices genéricas). [24] [25] Esto también significa que no hay impacto en el rendimiento de los lanzamientos en tiempo de ejecución y normalmente es costosoconversiones de boxeo . Cuando los tipos primitivos y de valor se utilizan como argumentos genéricos, obtienen implementaciones especializadas, lo que permite colecciones y métodos genéricos eficientes . Como en C ++ y Java, los tipos genéricos anidados como Dictionary <string, List <int>> son tipos válidos, sin embargo, se desaconsejan para las firmas de miembros en las reglas de diseño de análisis de código. [26]

.NET permite seis variedades de restricciones de tipo genérico utilizando la wherepalabra clave, incluida la restricción de tipos genéricos para que sean tipos de valor, clases, constructores e implementación de interfaces. [27] A continuación se muestra un ejemplo con una restricción de interfaz:

usando el  sistema ; muestra de clase{  vacío  estático Main () { int []  matriz  =  {  0 ,  1 ,  2 ,  3  }; MakeAtLeast < int > ( matriz ,  2 );  // Cambia la matriz a {2, 2, 2, 3} foreach  ( int  i  en  matriz ) Consola . WriteLine ( i );  // Imprimir resultados. Consola . ReadKey ( verdadero ); }  vacío  estático MakeAtLeast < T > ( lista T []  , T más bajo ) donde T : IComparable < T >       { para  ( int  i  =  0 ;  i  <  lista . Longitud ;  i ++) si  ( lista [ i ]. CompareTo ( más bajo )  <  0 ) lista [ i ]  =  más bajo ; }}

El MakeAtLeast()método permite operar sobre matrices, con elementos de tipo genérico T. La restricción de tipo del método indica que el método es aplicable a cualquier tipo Tque implemente la IComparable<T>interfaz genérica . Esto asegura un error de tiempo de compilación , si se llama al método si el tipo no admite la comparación. La interfaz proporciona el método genérico CompareTo(T).

El método anterior también podría escribirse sin tipos genéricos, simplemente usando el tipo no genérico Array. Sin embargo, dado que las matrices son contravariantes , la conversión no sería segura para los tipos y el compilador no podría encontrar ciertos errores posibles que de otro modo se detectarían al usar tipos genéricos. Además, el método necesitaría acceder a los elementos de la matriz como objects en su lugar, y requeriría la conversión para comparar dos elementos. (Para tipos de valor como tipos como inteste, se requiere una conversión de caja , aunque esto se puede solucionar usando la Comparer<T>clase, como se hace en las clases de colección estándar).

Un comportamiento notable de los miembros estáticos en una clase .NET genérica es la instanciación de miembros estáticos por tipo de tiempo de ejecución (consulte el ejemplo a continuación).

 // Una clase genérica  public  class  GenTest < T >  {  // Se creará una variable estática - para cada tipo en la reflexión  static  CountedInstances  OnePerType  =  new  CountedInstances (); // un miembro de datos  privado  T  mT ; // constructor simple  public  GenTest ( T  pT )  {  mT  =  pT ;  }  } // una clase  public  class  CountedInstances  {  // Variable estática - esto se incrementará una vez por instancia  public  static  int  Counter ; // constructor simple  public  CountedInstances ()  {  // aumenta el contador en uno durante la instanciación del objeto  CountedInstances . Counter ++;  }  } // punto de entrada del código principal  // al final de la ejecución, CountedInstances.Counter = 2  GenTest < int >  g1  =  new  GenTest < int > ( 1 );  GenTest < int >  g11  =  new  GenTest < int > ( 11 );  GenTest < int >  g111  =  new  GenTest < int > ( 111 );  GenTest < doble >  g2  =  nuevo  GenTest< doble > ( 1,0 );

Genericidad en Delphi [ editar ]

El dialecto Object Pascal de Delphi adquirió genéricos en la versión de Delphi 2007, inicialmente solo con el compilador .NET (ahora descontinuado) antes de agregarse al código nativo en la versión de Delphi 2009. La semántica y las capacidades de los genéricos de Delphi se basan en gran medida en las que tenían los genéricos en .NET 2.0, aunque la implementación es por necesidad bastante diferente. Aquí hay una traducción más o menos directa del primer ejemplo de C # que se muestra arriba:

 Muestra de programa ;{$ APPTYPE CONSOLE}usa  genéricos . Incumplimientos ;  // para IComparer <>tipo  TUtils  =  clase  clase  procedimiento  MakeAtLeast < T > ( Arr :  TArray < T >;  const  Lowest :  T ;  Comparer :  IComparer < T > ) ;  sobrecarga ;  procedimiento de clase  MakeAtLeast < T > ( Arr : TArray < T >; const Lowest : T ) ; sobrecarga      ;  terminar ; procedimiento de  clase TUtils . MakeAtLeast < T > ( Arr :  TArray < T >;  const  Lowest :  T ;  Comparer :  IComparer < T > ) ; var  I :  Entero ; comience  si  Comparer  =  nil  entonces  Comparer  : =  TComparer < T >. Predeterminado ;  para  I  : =  Bajo ( Arr)  a  Alto ( Arr )  hacer  si  Comparer . Compare ( Arr [ I ] ,  Lowest )  <  0  luego  Arr [ I ]  : =  Lowest ; terminar ; procedimiento de  clase TUtils . MakeAtLeast < T > ( Arr :  TArray < T >;  const  Lowest :  T ) ; comenzar  MakeAtLeast < T > ( Arr ,  Lowest ,  nil ) ; terminar ;var  Ints :  TArray < Entero >;  Valor :  entero ; begin  Ints  : =  TArray < Integer >. Crear ( 0 ,  1 ,  2 ,  3 ) ;  TUtils . MakeAtLeast < Entero > ( Ints ,  2 ) ;  de  Valor  en  Entrs  hacer  WriteLn ( Valor ) ;  ReadLn ; final.

Al igual que con C #, tanto los métodos como los tipos completos pueden tener uno o más parámetros de tipo. En el ejemplo, TArray es un tipo genérico (definido por el lenguaje) y MakeAtLeast un método genérico. Las restricciones disponibles son muy similares a las restricciones disponibles en C #: cualquier tipo de valor, cualquier clase, una clase o interfaz específica y una clase con un constructor sin parámetros. Varias restricciones actúan como una unión aditiva.

Genericidad en Free Pascal [ editar ]

Free Pascal implementó genéricos antes que Delphi, y con diferente sintaxis y semántica. Sin embargo, desde la versión 2.6.0 de FPC, la sintaxis estilo Delphi está disponible cuando se usa el modo de idioma {$ mode Delphi}. Por lo tanto, los programadores de Free Pascal pueden usar genéricos en el estilo que prefieran.

Ejemplo de Delphi y Free Pascal:

// Unidad de  estilo Delphi A ;{$ ifdef fpc}  {$ mode delphi} {$ endif}interfaztipo  TGenericClass < T >  =  función de clase  Foo ( const AValue : T ) : T ; terminar ;     implementaciónfunción  TGenericClass < T >. Foo ( const  AValue :  T ) :  T ; comenzar  Resultado  : =  AValue  +  AValue ; terminar ;fin .// Unidad  B de estilo ObjFPC de Free Pascal ;{$ ifdef fpc}  {$ mode objfpc} {$ endif}interfaztipo  genérico  TGenericClass < T >  =  función de clase  Foo ( const AValue : T ) : T ; terminar ;     implementaciónfunción  TGenericClass . Foo ( const  AValue :  T ) :  T ; comenzar  Resultado  : =  AValue  +  AValue ; terminar ;fin .// ejemplo de uso, programa  estilo Delphi TestGenDelphi ;{$ ifdef fpc}  {$ mode delphi} {$ endif}usa  A , B ;var  GC1 :  Una . TGenericClass < Entero >;  GC2 :  B . TGenericClass < String >; comenzará  GC1  : =  A . TGenericClass < Entero >. Crear ;  GC2  : =  B . TGenericClass < Cadena >. Crear ;  WriteLn ( GC1 . Foo ( 100 )) ;  // 200  WriteLn ( GC2 .Foo ( 'hola' )) ;  // hola hola  GC1 . Libre ;  GC2 . Libre ; fin .// ejemplo de uso, programa de  estilo ObjFPC TestGenDelphi ;{$ ifdef fpc}  {$ mode objfpc} {$ endif}usa  A , B ;// requerida en ObjFPC tipo  TAGenericClassInt  =  especializarse  A . TGenericClass < Entero >;  TBGenericClassString  =  especializarse  B . TGenericClass < String >; var  GC1 :  TAGenericClassInt ;  GC2 :  TBGenericClassString ; comience  GC1  : =  TAGenericClassInt . Crear ;  GC2  : =  TBGenericClassString . Crear ;  WriteLn ( GC1. Foo ( 100 )) ;  // 200  WriteLn ( GC2 . Foo ( 'hola' )) ;  // hola hola  GC1 . Libre ;  GC2 . Libre ; fin .

Lenguajes funcionales [ editar ]

Genericidad en Haskell [ editar ]

El mecanismo de clases de tipos de Haskell admite programación genérica. Seis de las clases de tipos predefinidas en Haskell (incluidos Eqlos tipos que se pueden comparar por igualdad y Showlos tipos cuyos valores se pueden representar como cadenas) tienen la propiedad especial de admitir instancias derivadas. Esto significa que un programador que defina un nuevo tipo puede afirmar que este tipo será una instancia de una de estas clases de tipos especiales, sin proporcionar implementaciones de los métodos de clase, como suele ser necesario al declarar instancias de clase. Todos los métodos necesarios se "derivarán", es decir, se construirán automáticamente, basándose en la estructura del tipo. Por ejemplo, la siguiente declaración de un tipo de árboles binariosestablece que debe ser una instancia de las clases Eqy Show:

data  BinTree  a  =  Leaf  a  |  Nodo  ( BinTree  a )  a  ( BinTree  a )  derivando  ( Eq ,  Show )

Esto da como resultado una función de igualdad ( ==) y una función de representación de cadena ( show) que se definen automáticamente para cualquier tipo de formulario BinTree Tsiempre que Tadmita esas operaciones.

El apoyo para los casos derivados de Eqy Showhace que sus métodos ==y showgenérica de una manera cualitativamente diferente de funciones para-métricamente polimórfica: estas "funciones" (más exactamente, las familias de tipo indexado de funciones) se pueden aplicar a los valores de varios tipos, y aunque se comportan de manera diferente para cada tipo de argumento, se necesita poco trabajo para agregar soporte para un nuevo tipo. Ralf Hinze (2004) ha demostrado que se puede lograr un efecto similar para las clases de tipos definidas por el usuario mediante ciertas técnicas de programación. Otros investigadores han propuesto enfoques para este y otros tipos de genérico en el contexto de Haskell y extensiones de Haskell (discutido a continuación).

PolyP [ editar ]

PolyP fue la primera extensión de lenguaje de programación genérico de Haskell . En PolyP, las funciones genéricas se denominan politípicas . El lenguaje introduce una construcción especial en la que tales funciones politípicas se pueden definir mediante inducción estructural sobre la estructura del functor de patrón de un tipo de datos regular. Los tipos de datos regulares en PolyP son un subconjunto de los tipos de datos de Haskell. Un tipo de datos regular t debe ser del tipo * → * , y si a es el argumento de tipo formal en la definición, entonces todas las llamadas recursivas a t deben tener la forma ta. Estas restricciones descartan los tipos de datos de tipo superior, así como los tipos de datos anidados, donde las llamadas recursivas son de una forma diferente. La función aplanar en PolyP se proporciona aquí como ejemplo:

 aplanar  ::  Regular  d  =>  d  a  ->  [ a ]  aplanar  =  cata  fl politípico  fl  ::  f  a  [ a ]  ->  [ a ]  caso  f  de  g + h  ->  ya sea  fl  fl  g * h  ->  \ ( x , y )  ->  fl  x  ++  fl  y  ()  ->  \ x  ->  []  Par  ->  \ x  ->  [ x ]  Rec  ->  \ x  ->  x d @ g  ->  concat  .  aplanar  .  pmap  fl  Con  t  ->  \ x  ->  [] cata  ::  Regular  d  =>  ( FunctorOf  d  a  b  ->  b )  ->  d  a  ->  b
Haskell genérico [ editar ]

Haskell genérico es otra extensión de Haskell , desarrollado en la Universidad de Utrecht en los Países Bajos . Las extensiones que proporciona son:

  • Los valores indexados por tipo se definen como un valor indexado sobre los diversos constructores de tipos Haskell (unidad, tipos primitivos, sumas, productos y constructores de tipos definidos por el usuario). Además, también podemos especificar el comportamiento de un tipo de valores indexados para un constructor específico usando casos de constructor , y reutilizar una definición genérica en otra usando casos predeterminados .

El valor indexado por tipo resultante se puede especializar en cualquier tipo.

  • Los tipos indexados por tipo son tipos indexados sobre tipos, definidos dando un caso para * y k → k ' . Las instancias se obtienen aplicando el tipo indexado por tipo a un tipo.
  • Las definiciones genéricas se pueden utilizar aplicándolas a un tipo o clase. A esto se le llama aplicación genérica . El resultado es un tipo o valor, según el tipo de definición genérica que se aplique.
  • La abstracción genérica permite definir definiciones genéricas abstrayendo un parámetro de tipo (de un tipo determinado).
  • Los tipos indexados por tipos son tipos que están indexados sobre los constructores de tipos. Estos se pueden utilizar para dar tipos a valores genéricos más complicados. Los tipos indexados por tipos resultantes se pueden especializar en cualquier tipo.

Como ejemplo, la función de igualdad en Generic Haskell: [28]

 escriba  la ecuación  {[  *  ]}  t1  t2  =  t1  ->  t2  ->  Bool  tipo  Eq  {[  k  ->  l  ]}  t1  t2  =  forall  u1  u2 .  Ecuación  {[  k  ]}  u1  u2  ->  Ecuación  {[  l  ]}  ( t1  u1 )  ( t2  u2 ) eq  { |  t  ::  k  | }  ::  Eq  {[  k  ]}  t  t  eq  { |  Unidad  | }  _  _  =  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  | }  =  ( == )

Limpiar [ editar ]

Clean ofrece PolyP genérico basado en programación y Haskell genérico compatible con GHC> = 6.0. Parametriza por tipo como esos pero ofrece sobrecarga.

Otros idiomas [ editar ]

La familia ML de lenguajes de programación admite la programación genérica a través del polimorfismo paramétrico y módulos genéricos llamados functores. Tanto Standard ML como OCaml proporcionan functors, que son similares a las plantillas de clase y a los paquetes genéricos de Ada. Las abstracciones sintácticas de esquema también tienen una conexión con la genéricaidad; de hecho, son un superconjunto de plantillas a la C ++.

Un módulo Verilog puede tomar uno o más parámetros, a los que se asignan sus valores reales tras la instanciación del módulo. Un ejemplo es una matriz de registro genérica donde el ancho de la matriz se da mediante un parámetro. Tal matriz, combinada con un vector de cable genérico, puede hacer un búfer genérico o un módulo de memoria con un ancho de bits arbitrario a partir de una implementación de módulo único. [29]

VHDL , derivado de Ada, también tiene capacidades genéricas.

Ver también [ editar ]

  • Concepto (programación genérica)
  • Evaluación parcial
  • Metaprogramación de plantillas
  • Tipo de polimorfismo

Referencias [ editar ]

  1. ^ Lee, Kent D. (15 de diciembre de 2008). Lenguajes de programación: un enfoque de aprendizaje activo . Springer Science & Business Media. págs. 9-10. ISBN 978-0-387-79422-8.
  2. ^ Milner, R .; Morris, L .; Newey, M. (1975). "Una lógica para funciones computables con tipos reflexivos y polimórficos". Actas de la conferencia sobre pruebas y mejoras de programas .
  3. ^ Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Patrones de diseño . Addison-Wesley. ISBN 0-201-63361-2.
  4. ^ Musser y Stepanov 1989 .
  5. ^ Musser, David R .; Stepanov, Alexander A. Programación genérica (PDF) .
  6. ^ Alexander Stepanov; Paul McJones (19 de junio de 2009). Elementos de programación . Addison-Wesley Professional. ISBN 978-0-321-63537-2.
  7. ^ Musser, David R .; Stepanov, Alexander A. (1987). "Una biblioteca de algoritmos genéricos en Ada". Actas de la Conferencia Internacional Anual ACM SIGAda de 1987 sobre Ada : 216–225. CiteSeerX 10.1.1.588.7431 . doi : 10.1145 / 317500.317529 . ISBN  0897912438. S2CID  795406 .
  8. ^ Alexander Stepanov y Meng Lee: la biblioteca de plantillas estándar. Informe técnico de HP Laboratories 95-11 (R.1), 14 de noviembre de 1995
  9. ^ Matthew H. Austern: Programación genérica y STL: uso y ampliación de la biblioteca de plantillas estándar de C ++. Addison-Wesley Longman Publishing Co., Inc. Boston, MA, EE. UU. 1998
  10. ^ Jeremy G. Siek, Lie-Quan Lee, Andrew Lumsdaine: The Boost Graph Library: Guía del usuario y manual de referencia. Addison-Wesley 2001
  11. Stepanov, Alexander. Breve historia de STL (PDF) .
  12. ^ a b Stroustrup, Bjarne. Evolución de un lenguaje en y para el mundo real: C ++ 1991-2006 (PDF) . doi : 10.1145 / 1238844.1238848 . S2CID 7518369 .  
  13. ^ Lo Russo, Graziano. "Una entrevista con A. Stepanov" .
  14. ^ Roland Backhouse; Patrik Jansson; Johan Jeuring; Lambert Meertens (1999). Programación genérica: una introducción (PDF) .
  15. ^ Lämmel, Ralf; Peyton Jones, Simon. "Deseche su plantilla: un patrón de diseño práctico para la programación genérica" (PDF) . Microsoft . Consultado el 16 de octubre de 2016 .
  16. ^ Gabriel Dos Reis; Jaakko Ja ̈rvi (2005). "¿Qué es la programación genérica? (Preimpresión LCSD'05)" (PDF) . Archivado desde el original (PDF) el 25 de diciembre de 2005.
  17. ^ R. García; J. Ja ̈rvi; A. Lumsdaine; J. Siek; J. Willcock (2005). "Un estudio comparativo extendido de soporte de lenguaje para programación genérica (preprint)". CiteSeerX 10.1.1.110.122 .  Cite journal requiere |journal=( ayuda )
  18. ^ Stroustrup, Dos Reis (2003): Conceptos - Opciones de diseño para la comprobación de argumentos de plantilla
  19. ^ Stroustrup, Bjarne (1994). "15.5 Evitar la replicación de código". El diseño y evolución de C ++ . Reading, Massachusetts: Addison-Wesley. págs. 346–348. Bibcode : 1994dec..book ..... S . ISBN 978-81-317-1608-3.
  20. ^ Brillante, Walter. "Tipos de Voldemort en D" . Dr. Dobbs . Consultado el 3 de junio de 2015 .
  21. ^ Construcción de software orientado a objetos, Prentice Hall, 1988, y Construcción de software orientado a objetos, segunda edición, Prentice Hall, 1997.
  22. Eiffel: The Language, Prentice Hall, 1991.
  23. ^ Historial genérico de .NET / C #: algunas fotos de febrero de 1999
  24. ^ C #: ayer, hoy y mañana: una entrevista con Anders Hejlsberg
  25. ^ Genéricos en C #, Java y C ++
  26. ^ Análisis de código CA1006: no anida tipos genéricos en firmas de miembros
  27. ^ Restricciones en los parámetros de tipo (Guía de programación de C #)
  28. ^ La guía del usuario genérica de Haskell
  29. ^ Verilog por ejemplo, sección El resto como referencia . Blaine C. Readler, Full Arc Press, 2011. ISBN 978-0-9834973-0-1 

Fuentes [ editar ]

  • Musser, DR ; Stepanov, AA (1989). "Programación genérica". En P. Gianni (ed.). Computación simbólica y algebraica: Simposio internacional ISSAC 1988 . Apuntes de conferencias en Ciencias de la Computación. 358 . págs. 13-25. doi : 10.1007 / 3-540-51084-2_2 . ISBN 978-3-540-51084-0.
  • Stroustrup, Bjarne (2007). Evolución de un lenguaje en y para el mundo real: C ++ 1991-2006 (PDF) . ACM HOPL 2007.
  • Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison-Wesley. Bibcode : 1995dper.book ..... G . ISBN 0-201-63361-2.

Lectura adicional [ editar ]

  • Gabriel Dos Reis y Jaakko Järvi, ¿Qué es la programación genérica? , LCSD 2005 .
  • Gibbons, Jeremy (2007). Backhouse, R .; Gibbons, J .; Hinze, R .; Jeuring, J. (eds.). Programación genérica de tipo de datos . Escuela de primavera sobre programación genérica de tipos de datos 2006. Notas de la conferencia en Ciencias de la Computación. 4719 . Heidelberg: Springer. págs. 1-71. CiteSeerX  10.1.1.159.1228 .
  • Meyer, Bertrand (1986). "Genericidad versus herencia". Actas de conferencias sobre sistemas, lenguajes y aplicaciones de programación orientada a objetos - OOPSLA '86 . págs. 391–405. doi : 10.1145 / 28697.28738 . ISBN 0897912047. S2CID  285030 .

Enlaces externos [ editar ]

  • generic-programming.org
  • Alexander A. Stepanov, artículos recopilados de Alexander A. Stepanov (creador del STL )
C ++ / D
  • Walter Bright, Plantillas revisitadas .
  • David Vandevoorde, Nicolai M Josuttis, Plantillas C ++: La guía completa , 2003 Addison-Wesley. ISBN 0-201-73484-2 
C # /. NET
  • Jason Clark, " Introducción a los genéricos en Microsoft CLR ", septiembre de 2003, MSDN Magazine , Microsoft.
  • Jason Clark, " Más sobre genéricos en Microsoft CLR ", octubre de 2003, MSDN Magazine , Microsoft.
  • M. Aamir Maniar, Generics.Net . Una biblioteca genérica de código abierto para C #.
Delphi / Object Pascal
  • Nick Hodges, " Guía de revisores de Delphi 2009 ", octubre de 2008, Embarcadero Developer Network , Embarcadero.
  • Craig Stuntz, " Delphi 2009 Generics and Type Constraints ", octubre de 2008
  • Dr. Bob, " Genéricos de Delphi 2009 "
  • Free Pascal : Guía de referencia de Free Pascal Capítulo 8: Genéricos , Michaël Van Canneyt, 2007
  • Delphi para Win32: Genéricos con Delphi 2009 Win32 , Sébastien DOERAENE, 2008
  • Delphi para .NET: Delphi Generics , Felix COLIBRI, 2008
Eiffel
  • Documento de especificaciones Eiffel ISO / ECMA
Haskell
  • Johan Jeuring, Sean Leather, José Pedro Magalhães y Alexey Rodríguez Yakushev. Bibliotecas para programación genérica en Haskell . Universidad de Utrecht.
  • Dæv Clarke, Johan Jeuring y Andres Löh, la guía del usuario genérica de Haskell
  • Ralf Hinze, " Genéricos para las masas ", en Actas de la Conferencia Internacional ACM SIGPLAN sobre Programación Funcional (ICFP), 2004.
  • Simon Peyton Jones , editor, The Haskell 98 Language Report , revisado en 2002.
  • Ralf Lämmel y Simon Peyton Jones , "Scrap Your Boilerplate: A Practical Design Pattern for Generic Programming", en Actas del Taller internacional ACM SIGPLAN sobre tipos de diseño e implementación de lenguajes (TLDI'03), 2003. (Véase también el sitio web dedicado a esta investigación )
  • Andres Löh, Explorando Haskell genérico , tesis doctoral, 2004 Universidad de Utrecht . ISBN 90-393-3765-9 
  • Haskell genérico: un lenguaje para programación genérica
Java
  • Gilad Bracha, Genéricos en el lenguaje de programación Java , 2004.
  • Maurice Naftalin y Philip Wadler, Java Generics and Collections, 2006, O'Reilly Media, Inc. ISBN 0-596-52775-6 
  • Peter Sestoft, Java Precisely, segunda edición, 2005 MIT Press. ISBN 0-262-69325-9 
  • Programación genérica en Java , 2004 Sun Microsystems, Inc.
  • Angelika Langer, Preguntas frecuentes sobre genéricos de Java