1)
A continuación se muestra una función de Python summation
, que puede realizar una suma de cubos / cuadrados / .., operaciones similares .
def identity(k): return k def cube(k): return pow(k, 3) def square(k): return pow(k,2) def summation(n, term): if n == 0: return 0 else: return term(n) + summation(n-1, term) def sum_cubes(n): return summation(n, cube) if __name__ == "__main__": sum = sum_cubes(4) print(sum) """ In C, We can implement the same using function pointers. Goal is, to perform similar operations(Sum of ..) using single function summation()"""
2)
Considere, a continuación, ordenar la API de C,
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Aquí, qsort
puede ordenar datos de cualquier tipo , matriz de flotantes / nombres de archivo en un directorio / cadenas / …
Pregunta:
¿Cómo definir una función genérica?
¿Es summation
una función genérica?
o
¿Es qsort
una función genérica?
o
Dados dos ejemplos, ¿es función genérica una terminología no válida?
Nota: Término de motivación para qsort
o cualquier función de clasificación que yo diseño
Comentarios
- ¿Qué definición de » función genérica » ¿ha leído que no ‘ entiende? Sería útil si publicaras eso en lugar de escribir un montón de código.
- El término de teoría de tipos para el tipo de genérico en el que una función funciona para cualquier tipo sin restricciones y sin conocimiento del tipo específico es polimorfismo paramétrico . La función de identidad es genérica de esta manera.
- En algunos lenguajes (como Java) » función genérica » tiene una definición técnica específica. Pero este no es el caso en Python, por lo que la » función genérica » no tiene un significado bien definido. No significa que sea » terminología inválida «, solo que debes tener en cuenta el contexto cuando uses el término.
- @AndresF. Javascrpt también usa mucho esta terminología de función genérica . Porque es posible que tenga una función que tome cualquier elemento html para procesar (ejemplo: eliminar todos los elementos secundarios de un elemento html dado)
Respuesta
Hay varios significados para» genérico «.
Definición informal
«genérico» en el lenguaje cotidiano algo que comparte propiedades comunes pero es menos específico de alguna manera.
Bajo esta perspectiva, podría considerar qsort()
como genérico: el código de esta función es capaz de ordenar cualquier estructura de datos de tamaño fijo para la que se puede definir una función de comparación mediante el algoritmo QSORT.
Lo mismo se aplica a su función summation()
, que resume los términos obtenidos utilizando cualquier función con un parámetro.
Definición formal
Los lenguajes de programación como C ++ o Java permiten la programación genérica con el uso de plantillas o genéricos:
Definición del estándar C ++ 14 : Una plantilla define una familia de clases o funciones o un alias para una familia de tipos.
El principio es que la implementación de una clase o función puede parametrizarse por tipos.
De acuerdo con este punto de vista más formal, qsort()
no es una función genérica. Su implementación no necesita determinar ningún tipo en la compilación, y su comportamiento es independiente del tipo. Lo único que necesita es el tamaño de los elementos que se ordenan, y este tamaño es un argumento ordinario que se procesa en tiempo de ejecución.
Para un lenguaje que no está escrito de forma estática como Python , «no estoy seguro de qué responder por summation()
. Creo que no es genérico porque su implementación y su comportamiento no dependen del tipo: esta función es solo una función de orden superior, con el argumento term
siendo una función. No utiliza ninguna característica que altere el comportamiento de esta función en función de los tipos.
Para ilustrar una función genérica, puede echar un vistazo a la función estándar de C ++ std::sort()
: su implementación depende del tipo de sus argumentos (y opcionalmente una función de comparación con argumentos de un tipo determinado). Al usar las características de las plantillas de C ++, puede clasificar cualquier contenedor de cualquier tipo, con la condición de que tenga los operadores / funciones miembro / rasgos / iteradores que son requeridos por la implementación de la función genérica.
¿Puede un lenguaje escrito dinámicamente tener funciones genéricas
El lenguaje escrito dinámicamente requiere código menos genérico que los lenguajes tipados estáticamente.
Por ejemplo, si tiene un contenedor de objetos de tipo dinámico, una función qsort podría ordenar genéricamente el contenedor, siempre que se pueda comparar cualquier combinación de dos elementos en el contenedor.
Pero incluso en un entorno tan flexible, la programación genérica –dependiente del tipo– podría ser útil. El caso de uso típico son los métodos múltiples, donde el comportamiento o el código depende del tipo de argumentos o incluso de la combinación de tipos (como para determinar la intersección entre dos formas diferentes). Para obtener información adicional, consulte:
- Paquete de programación genérico para Python: multidispatching
- Hacer funciones genéricas utilizables en smalltalk
- Ejemplo de función genérica en Common Lisp
Comentarios
- No estoy seguro. ¿Por qué comparamos Genéricos (principalmente utilizado para evitar la conversión de tipos en Java & ayuda a realizar polimorfismo) con la definición de función genérica?
- @overexchange Creo que Java también ofrece programación genérica, incluidos métodos genéricos (consulte la especificación o el tutorial ). Sin embargo, ‘ he editado ligeramente la parte de la definición para abordar su comentario.
- El paquete Generic de Python no tiene nada que ver con funciones genéricas. Excepto que comparten el mismo adjetivo.
- @Killian if programación genérica , trata sobre la idea de abstraerse de algoritmos concretos y eficientes para obtener algoritmos genéricos que se pueden combinar con diferentes representaciones de datos , creo que los métodos múltiples en ese paquete deben estar en, don ‘ ¿no lo crees?
Respuesta
Las funciones genéricas toman el tipo de al menos un argumento de función genéricamente en tiempo de compilación. Es decir, el compilador descubre qué tipo se usa en un lugar determinado y aplica exactamente este tipo donde se usa en la función. P.ej. si tiene un argumento genérico en su función que se usa con un operador +
, el tipo debe tener los métodos adecuados. Para cadenas / matrices, esto sería en muchos casos una concatenación y para un entero / flotante una adición. El compilador puede detectar que se aplica la operación correcta. Su rutina C no es genérica en ese sentido, ya que es el programador quien aplica cierta información de tamaño y no el compilador detectando el tipo y usando el tamaño correcto.
Por ejemplo, en algún lenguaje ficticio
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
Aquí el compilador detecta en el primer caso que se aplican dos cadenas y expandirá internamente algo como
func add(p1:string, p2:string)
y tratar +
como una concatenación, mientras que en el segundo caso se expandiría
func add(p1:int, p2:int)
según se proporciona Parámetros enteros. Genérico significa, el compilador genera código individual durante el tiempo de compilación. Python, por ejemplo, no tiene tipo y haría ese tipo de sustitución durante el tiempo de ejecución. Significa: Python no tiene funciones genéricas ya que todo es genérico.
Comentarios
- No entendí tu idea. ¿Quieres decir + es una función genérica, la sintaxis en C ++?
- Las funciones que toman argumentos de función son de orden superior función s en el mundo de Python / JavaScript. En C, necesitamos punteros de función, para lo mismo.
- Vea mi edición anterior.
- Entonces, ¿qué tipo de función es
summation
, ¿Función de orden superior? ¿y nada más que eso? - Hay muchas definiciones de lo que es genérico. Stroustrup, por ejemplo, lo define como » programación usando tipos como parámetros «. Para la referencia de wikipedia, ‘ prefiero ir a: en.wikipedia.org/wiki/Generic_programming
Respuesta
Voy a comenzar esto desde la perspectiva de C ++, luego trabajaré en C.
En lenguajes de tipado estático como C, C ++, Java, etc., una función «genérica» le permite especificar las operaciones de la función una vez , usando marcadores de posición para cualquier tipo que pueda variar entre diferentes llamadas (lo que significa que funciones como qsort
y bsearch
son definitivamente no funciones genéricas). Idealmente, también le gustaría que el compilador detecte automáticamente cualquier llamada a esta función genérica y genere el código real según sea necesario.
C ++ facilita esto 1 al ofrecer plantillas :
template <typename T> T summation( T *values, size_t numValues ) { T result = 0; for ( size_t i = 0; i < numValues; i++ ) result += values[i]; return result; }
T
es un marcador de posición para cualquier tipo 2 , por lo que puede llamarlo como
int ivals[] = {1,2,3,4,5,6,7,8,9}; double dvals[] = {1,2,3,4,5,6,7,8,9}; int sumi = summation( ivals, 10 ); double sumd = summation( dvals, 10 );
Cuando se compila el código, el compilador ve las dos llamadas a summation
y deduce los tipos de argumentos. Para cada tipo diferente, genera una nueva instancia de la función, dándole un nombre único:
int summation_i( int *values, size_t numValues ) // actual compilers will generate { // more complex "mangled" names int result = 0; // than this ... } double summation_d( double *values, size_t numValues ) { double result = 0; ... }
Luego genera código de modo que el resultado de summation_i
se asigna a sumi
y summation_d
se asigna a sumd
.
C no ofrece nada similar a la función de plantilla. Tradicionalmente, hemos atacado la programación genérica de una de estas dos formas: utilizando macros o void *
en todas partes y delegando operaciones con reconocimiento de tipos a otras funciones.
Aquí hay un mal ejemplo de una solución basada en macros:
#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(t,x,s) summation_##t(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(int, ivals, 5); double sumd = SUMMATION(double, dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; }
El SUMMATION_DEF
es aproximadamente similar a una plantilla en el sentido de que especifica las operaciones de la función, utilizando el parámetro macro t
como marcador de posición de tipo. También usamos t
como parte del nombre de la función: ##
es el operador de pegado de tokens, y el preprocesador expandirá t
y agregue ese valor al nombre de la función 3 .
En lo que se diferencia de C ++ es en el hecho de que una macro es solo una sustitución de texto tonta. No activa cualquier operación especial por parte del compilador. Las instancias de funciones reales no se generan automáticamente en función de las invocaciones de la macro SUMMATION
; tenemos que generar explícitamente las funciones que queremos (de ahí el SUMMATION_DEF(int)
y SUMMATION_DEF(double)
antes de main
). También significa que cuando llamamos a summation_xxx
a través de la macro SUMMATION
, tenemos que pasar el tipo como parte de la lista de argumentos de la macro, de modo que se llame a la función correcta. Qué molestia.
La El estándar C 2011 agregó la palabra clave _Generic
, que puede hacer la vida un poco más fácil en ese sentido:
#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(x,s) _Generic((x), \ int * : summation_int, \ double * : summation_double \ )(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(ivals, 5); double sumd = SUMMATION(dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; }
El _Generic
La palabra clave le permite evaluar expresiones basadas en tipos ; por lo tanto, si el tipo del primer argumento para SUMMATION
es int *
, lo llamamos summation_int
; es «s it» s double *
, llamamos summation_double
. De esta forma no tenemos que especificar el nombre del tipo en los argumentos de la macro.
El otro enfoque, como «ha visto, es usar void *
y delegar operaciones con reconocimiento de tipos a otras funciones. Como dije anteriormente,» No es una programación realmente «genérica», ya que debe implementar manualmente cada función de comparación para cada tipo. No puede simplemente codificarlo una vez y terminar con él. Y al usar void *
, básicamente arroja la seguridad de tipos por la ventana y al tráfico que se aproxima.
Y antes de que alguien se queje, no, ninguna de estas funciones de suma verifica o trata el desbordamiento aritmético. Ese es un tema para otro día.
- Para definiciones suficientemente vagas de «fácil». El lenguaje de metaprogramación utilizado para admitir plantillas es Turing-complete, por lo que puede hacer cosas * realmente increíbles * e imposibles de entender con él.
- Para definiciones suficientemente vagas de «cualquier tipo». Tenga en cuenta que cualquier tipo que utilice debe admitir el operador
+=
, de lo contrario, el compilador le gritará. - Este código se se romperá para tipos como
unsigned int
olong double
ya que tienen espacios en blanco en el nombre. No conozco de inmediato la solución a ese problema, y he dedicado suficiente tiempo a esta respuesta tal como está.