En programación informática , un argumento predeterminado es aquel que el programador no necesita especificar. En la mayoría de los lenguajes de programación, las funciones pueden aceptar uno o más argumentos. Normalmente, cada argumento debe especificarse por completo (como en el lenguaje de programación C [ 1 ] ). Lenguajes más recientes (por ejemplo, C++ ) permiten al programador especificar argumentos predeterminados que siempre tienen un valor, incluso si no se especifica ninguno al llamar a la función.
Argumentos predeterminados en C++
Considere la siguiente declaración de función:
int foo ( int a , int b , int c = 12 );Esta función toma tres argumentos, de los cuales el último tiene un valor predeterminado de doce. El programador puede llamar a esta función de dos maneras:
int resultado = foo ( 1 , 2 , 3 ); resultado = foo ( 1 , 2 );En el primer caso, el valor del argumento cse especifica explícitamente. En el segundo caso, el argumento se omite y 12se utiliza el valor predeterminado. Para la función llamada, no hay forma de saber si el argumento fue especificado por quien la llama o si se utilizó el valor predeterminado.
El método mencionado anteriormente es especialmente útil cuando se desea establecer criterios predeterminados para que la función pueda llamarse con o sin parámetros. Considere lo siguiente:
importar std ;usando std :: ostream ;void printGreeting ( ostream & stream = std :: cout ) { // Esto imprime un mensaje en el flujo dado. std :: println ( stream , "¡Hola, mundo!" ); }La llamada a la función imprimirá por defecto "¡Hola, mundo!" en la salida estándar . Por otro lado, ahora se puede pasar cualquier objeto de tipo a la misma función y esta imprimirá en la salida especificada en lugar de en la salida estándar. Mientras tanto, establece el parámetro en , y así imprime la salida en la salida de error estándar.printGreeting();std::ostreamprintGreeting(std::cerr);streamstd::cerr
Debido a que los valores de los argumentos predeterminados se "rellenan" en el punto de llamada en lugar de en el cuerpo de la función que se llama, las funciones virtuales toman sus valores de argumentos predeterminados del tipo estático del puntero o referencia a través del cual se realiza la llamada, en lugar del tipo dinámico del objeto que proporciona el cuerpo de la función virtual.
importar std ;usando std :: pair ;struct Base { virtual constexpr pair < int , int > foo ( int x = 1 ) const { return { x , 1 }; } };struct Derived : public Base { constexpr pair < int , int > foo ( int x = 2 ) const override { return { x , 2 }; } };int main () { Derivado d ; const Base & b = d ;static_assert ( d.foo ( ) == std :: make_pair ( 2 , 2 ) ); static_assert ( b.foo ( ) == std :: make_pair ( 1 , 2 ) ); }Métodos sobrecargados
Algunos lenguajes, como Java , no tienen argumentos predeterminados. Sin embargo, se puede simular el mismo comportamiento mediante la sobrecarga de métodos , creando métodos sobrecargados con el mismo nombre que aceptan diferentes cantidades de argumentos; las versiones con menos argumentos simplemente llaman a las versiones con más argumentos, utilizando los argumentos predeterminados como argumentos faltantes.
int foo ( int a , int b , int c ) { // implementación principal aquí }int foo ( int a , int b ) { return foo ( a , b , 12 ); }Sin embargo, además de otras desventajas, dado que los argumentos predeterminados no se modelan en el sistema de tipos, el tipo de una función de devolución de llamada (también conocida como función de orden superior ) no puede expresar que acepta ninguna de las sobrecargas ni simular los argumentos predeterminados con funciones sobrecargadas. Mientras que en JavaScript la definición de la función no sobrecargada puede sustituir el valor predeterminado cuando el valor de entrada es undefined(independientemente de si fue implícitamente undefineda través de la ausencia del argumento en el sitio de llamada o un undefinedvalor pasado explícitamente); lo cual se modela como un tipo de parámetro de argumento opcional ?:en TypeScript . La solución de JavaScript no se resuelve estáticamente (es decir, no en tiempo de compilación, razón por la cual TypeScript modela solo la opcionalidad y no los valores predeterminados en la firma de tipo de la función ) por lo tanto incurre en una sobrecarga adicional en tiempo de ejecución, aunque proporciona más flexibilidad ya que las funciones de devolución de llamada pueden controlar de forma independiente sus valores predeterminados en lugar de ser dictados centralmente por la firma de tipo (firma de tipo de la función de devolución de llamada en la) firma de tipo de la función que ingresa la función de devolución de llamada. La solución de TypeScript, por ejemplo, se puede simular en Java con el java.util.Optional<T>tipo excepto que el análogo de un implícito undefinedpara cada argumento ausente es un explícito Optional.<Integer>absent()en el sitio de la llamada; un valor ausente se da con Optional.empty().
Parámetros de tipo predeterminados
En algunos lenguajes, como C++, el parámetro de tipo de un genérico puede tener un tipo predeterminado.
plantilla < typename T = int > clase Box { privado : T valor ; público : explícito Box ( T valor ) : valor { valor } {}// ... };Caja < doble > b1 ( 13.5 ); Caja <> b2 ( 5 ); // Esta es Caja<int>Evaluación
Para cada llamada a función, se deben pasar valores de argumentos predeterminados a la función llamada. Si un valor de argumento predeterminado contiene efectos secundarios, es importante cuándo se evalúan dichos efectos: una vez para todo el programa (en tiempo de análisis, compilación o carga) o una vez por llamada a función, en el momento de la llamada.
Python es un lenguaje notable que evalúa las expresiones en los argumentos predeterminados una sola vez, al momento de evaluar la declaración de la función. Si se desea la evaluación por llamada a la función, se puede replicar haciendo que el argumento predeterminado sea un valor centinela , como None, y luego haciendo que el cuerpo de la función evalúe los efectos secundarios del valor predeterminado solo si se pasó el valor centinela.
Por ejemplo:
importar aleatoriodef eager ( a : float = random.random ( )) - > float : return ax : float = eager () y : float = eager () assert x == ydef lazy ( a : float | None = None ) -> float : if a is None : a = random . random () return ax : float = lazy () y : float = lazy () assert x != yMedida
En general, un argumento predeterminado se comportará de forma idéntica a un argumento pasado como parámetro o a una variable local declarada al inicio de la función, y tendrá el mismo alcance y duración (tiempo de vida) que un parámetro u otra variable local, es decir, una variable automática que se libera al finalizar la función.
En otros casos, un argumento predeterminado puede asignarse estáticamente. Si la variable es mutable, conservará su valor entre llamadas a funciones, al igual que una variable estática .
Este comportamiento se encuentra en Python para tipos mutables, como las listas. Al igual que con la evaluación, para asegurar el mismo alcance que una variable local, se puede usar un valor centinela:
def eager ( a : lista [ int ] = []) -> lista [ int ]: return ax : lista [ int ] = eager () x += [ 1 ] assert eager () == [ 1 ]def lazy ( a : lista [ int ] | None = None ) -> lista [ int ]: if a is None : a = [] return ax : lista [ int ] = lazy () x += [ 1 ] assert lazy () == []Referencias
- ↑ Leslie, Martin. "Parámetros predeterminados" . Referencia de programación en C++ . Archivado del original el 9 de octubre de 2011. Consultado el 13 de enero de 2012 .
- C++
- Subrutinas