language agnostic - ¿Cuáles son algunos otros lenguajes que apoyan la "especialización parcial"?
language-agnostic generics (6)
La especialización de plantilla parcial es uno de los conceptos más importantes para la programación genérica en C ++. Por ejemplo: para implementar una función de intercambio genérica:
template <typename T>
void swap(T &x, T &y) {
const T tmp = x;
y = x;
x = tmp;
}
Para especializarlo para que un vector sea compatible con O (1) swap:
template <typename T, class Alloc>
void swap(vector<T, Alloc> &x, vector<T, Alloc> &y) { x.swap(y); }
Por lo tanto, siempre puede obtener un rendimiento óptimo cuando llama a swap (x, y) en una función genérica;
Muy apreciado, si puede publicar el equivalente (o el ejemplo canónico de especialización parcial del idioma si el idioma no admite el concepto de intercambio) en idiomas alternativos.
EDITAR : por lo que parece que muchas personas que respondieron / comentaron realmente no sabían qué es la especialización parcial, y que el ejemplo genérico de intercambio parece interferir en la comprensión de algunas personas. Un ejemplo más general sería:
template <typename T>
void foo(T x) { generic_foo(x); }
Una especialización parcial sería:
template <typename T>
void foo(vector<T> x) { partially_specialized_algo_for_vector(x); }
Una especialización completa sería:
void foo(vector<bool> bitmap) { special_algo_for_bitmap(bitmap); }
¿Por qué esto es importante? porque puedes llamar a foo (cualquier cosa) en una función genérica:
template <typename T>
void bar(T x) {
// stuff...
foo(x);
// more stuff...
}
y obtenga la implementación más adecuada en tiempo de compilación. Esta es una forma de que C ++ logre la abstracción con una mínima penalización de rendimiento.
Espero que ayude a aclarar el concepto de "especialización parcial". En cierto modo, así es como C ++ escribe la coincidencia de patrones sin necesidad de la sintaxis de concordancia explícita de patrones (por ejemplo, la palabra clave de concordancia en Ocaml / F #), que a veces se interpone en el camino para la programación genérica.
DO#:
void Swap<T>(ref T a, ref T b) {
var c = a;
a = b;
b = c;
}
Supongo que la versión (pura) de Haskell sería:
swap :: a -> b -> (b,a)
swap a b = (b, a)
En realidad, puedes (no del todo, ver abajo) hacerlo en C # con métodos de extensión:
public Count (this IEnumerable<T> seq) {
int n = 0;
foreach (T t in seq)
n++;
return n;
}
public Count (this T[] arr) {
return arr.Length;
}
Luego, llamar a array.Count()
usará la versión especializada. "No del todo" se debe a que la resolución depende del tipo de array
estática, no del tipo de tiempo de ejecución. Es decir, usará la versión más general:
IEnumerable<int> array = SomethingThatReturnsAnArray();
return array.Count();
Haskell tiene instancias superpuestas como una extensión:
class Sizable a where
size :: a -> Int
instance Collection c => Sizable c where
size = length . toList
es una función para encontrar el tamaño de cualquier colección, que puede tener instancias más específicas:
instance Sizable (Seq a) where
size = Seq.length
Ver también Advanced Overlap en HaskellWiki .
Java tiene genéricos, que te permiten hacer tipos similares de cosas.
Me temo que C # no admite la especialización de plantillas parciales.
La especialización parcial de la plantilla significa:
Tienes una clase base con dos o más plantillas (genéricos / parámetros de tipo). Los parámetros de tipo serían <T, S>
En una clase derivada (especializada), indica el tipo de uno de los parámetros de tipo. Los parámetros de tipo podrían verse así <T, int>.
Entonces, cuando alguien usa (crea un objeto de) la clase donde el último parámetro de tipo es un int, se usa la clase derivada.
D admite especialización parcial:
- Descripción general del lenguaje
- Comparación de características de la plantilla (con C ++ 98 y 0x).
(busque "parcial" en los enlaces anteriores).
El segundo enlace en particular le dará un desglose muy detallado de lo que puede hacer con la especialización de plantillas, no solo en D sino también en C ++.
Aquí hay un ejemplo específico de D de swap
. Debe imprimir el mensaje para el intercambio especializado para la clase Thing
.
import std.stdio; // for writefln
// Class with swap method
class Thing(T)
{
public:
this(T thing)
{
this.thing = thing;
}
// Implementation is the same as generic swap, but it will be called instead.
void swap(Thing that)
{
const T tmp = this.thing;
this.thing = that.thing;
that.thing = tmp;
}
public:
T thing;
}
// Swap generic function
void swap(T)(ref T lhs, ref T rhs)
{
writefln("Generic swap.");
const T tmp = lhs;
lhs = rhs;
rhs = tmp;
}
void swap(T : Thing!(U))(ref T lhs, ref T rhs)
{
writefln("Specialized swap method for Things.");
lhs.swap(rhs);
}
// Test case
int main()
{
auto v1 = new Thing!(int)(10);
auto v2 = new Thing!(int)(20);
assert (v1.thing == 10);
assert (v2.thing == 20);
swap(v1, v2);
assert (v1.thing == 20);
assert (v2.thing == 10);
return 0;
}