5.8. El orden no importa
En algunos lenguajes, una función debe estar declarada antes de usarse. Esto no es necesario en C#. Por ejemplo, podríamos rescribir el fuente anterior, de modo que "Main" aparezca en primer lugar y "duplica" aparezca después, y seguiría compilando y funcionando igual:
/*---------------------------*/ /* Ejemplo en C# nº 55: */ /* ejemplo55.cs */ /* */ /* Función tras Main */ /* */ /* Introduccion a C#, */ /* Nacho Cabanes */ /*---------------------------*/ using System; public class Ejemplo55 { public static void Main() { int n = 5; Console.WriteLine("n vale {0}", n); duplica(ref n); Console.WriteLine("Ahora n vale {0}", n); } public static void duplica(ref int x) { Console.WriteLine(" El valor recibido vale {0}", x); x = x * 2; Console.WriteLine(" y ahora vale {0}", x); } }
5.9. Algunas funciones útiles
5.9.1. Números aleatorios
En un programa de gestión o una utilidad que nos ayuda a administrar un sistema, no es habitual que podamos permitir que las cosas ocurran al azar. Pero los juegos se encuentran muchas veces entre los ejercicios de programación más completos, y para un juego sí suele ser conveniente que haya algo de azar, para que una partida no sea exactamente igual a la anterior.
Generar números al azar ("números aleatorios") usando C# no es difícil: debemos crear un objeto de tipo "Random", y luego llamaremos a "Next" para obtener valores entre dos extremos:
// Creamos un objeto random Random r = new Random(); // Generamos un número entre dos valores dados int aleatorio = r.Next(1, 100);
Podemos hacer que sea realmente un poco más aleatorio si en la primera orden le indicamos que tome como semilla el instante actual:
Random r = new Random(DateTime.Now.Millisecond);
De hecho, una forma muy simple de obtener un número "casi al azar" entre 0 y 999 es tomar las milésimas de segundo de la hora actual:
int falsoAleatorio = DateTime.Now.Millisecond;
Vamos a ver un ejemplo, que muestre en pantalla un número al azar entre 1 y 10:
/*---------------------------*/ /* Ejemplo en C# nº 56: */ /* ejemplo56.cs */ /* */ /* Números al azar */ /* */ /* Introduccion a C#, */ /* Nacho Cabanes */ /*---------------------------*/ using System; public class Ejemplo56 { public static void Main() { Random r = new Random(DateTime.Now.Millisecond); int aleatorio = r.Next(1, 10); Console.WriteLine("Un número entre 1 y 10: {0}", aleatorio); } }
Ejercicios propuestos:
- Crear un programa que genere un número al azar entre 1 y 100. El usuario tendrá 6 oportunidades para acertarlo.
- Mejorar el programa del ahorcado propuesto en el apartado 4.4.8, para que la palabra a adivinar no sea tecleado por un segundo usuario, sino que se escoja al azar de un "array" de palabras prefijadas (por ejemplo, nombres de ciudades).
5.9.2. Funciones matemáticas
En C# tenemos muchas funciones matemáticas predefinidas, como:
- Abs(x): Valor absoluto
- Acos(x): Arco coseno
- Asin(x): Arco seno
- Atan(x): Arco tangente
- Atan2(y,x): Arco tangente de y/x (por si x o y son 0)
- Ceiling(x): El valor entero superior a x y más cercano a él
- Cos(x): Coseno
- Cosh(x): Coseno hiperbólico
- Exp(x): Exponencial de x (e elevado a x)
- Floor(x): El mayor valor entero que es menor que x
- Log(x): Logaritmo natural (o neperiano, en base "e")
- Log10(x): Logaritmo en base 10
- Pow(x,y): x elevado a y
- Round(x, cifras): Redondea un número
- Sin(x): Seno
- Sinh(x): Seno hiperbólico
- Sqrt(x): Raíz cuadrada
- Tan(x): Tangente
- Tanh(x): Tangente hiperbólica
(casi todos ellos usan parámetros X e Y de tipo "double")
y una serie de constantes como
- E, el número "e", con un valor de 2.71828...
- PI, el número "Pi", 3.14159...
Todas ellas se usan precedidas por "Math."
La mayoría de ellas son específicas para ciertos problemas matemáticos, especialmente si interviene la trigonometría o si hay que usar logaritmos o exponenciales. Pero vamos a destacar las que sí pueden resultar útiles en situaciones más variadas:
- La raíz cuadrada de 4 se calcularía haciendo x = Math.Sqrt(4);
- La potencia: para elevar 2 al cubo haríamos y = Math.Pow(2, 3);
- El valor absoluto: para trabajar sólo con números positivos usaríamos n = Math.Abs(x);
Ejercicios propuestos:
- Crear un programa que halle cualquier raíz de un número. El usuario deberá indicar el número (por ejemplo, 2) y el índice de la raíz (por ejemplo, 3 para la raíz cúbica). Pista: hallar la raíz cúbica de 2 es lo mismo que elevar 2 a 1/3.
- Crear un programa que resuelva ecuaciones de segundo grado, del tipo ax2 + bx + c = 0 El usuario deberá introducir los valores de a, b y c. Pista: la solución se calcula con x = -b ? raíz (b2 – 4•a•c) / 2•a
5.9.3. Pero hay muchas más funciones…
PPero en C# hay muchas más funciones de lo que parece. De hecho, salvo algunas palabras reservadas (int, float, string, if, switch, for, do, while...), gran parte de lo que hasta ahora hemos llamado "órdenes", son realmente "funciones", como Console.ReadLine o Console.WriteLine. Nos iremos encontrando con otras funciones a medida que avancemos.
5.10. Recursividad
Una función recursiva es aquella que se define a partir de ella misma. Dentro de las matemáticas tenemos varios ejemplos. Uno clásico es el "factorial de un número":
n! = n • (n-1) • (n-2) • ... • 3 • 2 • 1
(por ejemplo, el factorial de 4 es 4 • 3 • 2 • 1 = 24)
Si pensamos que el factorial de n-1 es
(n-1)! = (n-1) • (n-2) • (n-3) • ... • 3 • 2 • 1
Entonces podemos escribir el factorial de un número a partir del factorial del siguiente número:
n! = n • (n-1)!
Esta es la definición recursiva del factorial, ni más ni menos. Esto, programando, se haría:
/*---------------------------*/ /* Ejemplo en C# nº 57: */ /* ejemplo57.cs */ /* */ /* Funciones recursivas: */ /* factorial */ /* */ /* Introduccion a C#, */ /* Nacho Cabanes */ /*---------------------------*/ using System; public class Ejemplo57 { public static long fact(int n) { if (n==1) // Aseguramos que termine return 1; return n * fact (n-1); // Si no es 1, sigue la recursión } public static void Main() { int num; Console.WriteLine("Introduzca un número entero: "); num = System.Convert.ToInt32(System.Console.ReadLine()); Console.WriteLine("Su factorial es: {0}", fact(num)); } }
Dos consideraciones importantes:
- Atención a la primera parte de la función recursiva: es MUY IMPORTANTE comprobar que hay salida de la función, para que nuestro programa no se quede dando vueltas todo el tiempo y deje el ordenador (o la tarea actual) "colgado".
- Los factoriales crecen rápidamente, así que no conviene poner números grandes: el factorial de 16 es 2.004.189.184, luego a partir de 17 podemos obtener resultados erróneos, si usamos números enteros "normales".
¿Qué utilidad tiene esto? Pues más de la que parece: muchos problemas complicados se pueden expresar a partir de otro más sencillo. En muchos de esos casos, ese problema se podrá expresar de forma recursiva. Más adelante veremos algún otro ejemplo.
Ejercicios propuestos:
- Crear una función que calcule el valor de elevar un número entero a otro número entero (por ejemplo, 5 elevado a 3 = 53 = 5 •5 •5 = 125). Esta función se debe crear de forma recursiva.
- Como alternativa, crear una función que calcule el valor de elevar un número entero a otro número entero de forma NO recursiva (lo que llamaremos "de forma iterativa"), usando la orden "for".
- Crear un programa que emplee recursividad para calcular un número de la serie Fibonacci (en la que los dos primeros elementos valen 1, y para los restantes, cada elemento es la suma de los dos anteriores).
- Crear un programa que emplee recursividad para calcular la suma de los elementos de un vector.
- Crear un programa que emplee recursividad para calcular el mayor de los elementos de un vector.
- Crear un programa que emplee recursividad para dar la vuelta a una cadena de caracteres (por ejemplo, a partir de "Hola" devolvería "aloH").
- Crear, tanto de forma recursiva como de forma iterativa, una función diga si una cadena de caracteres es simétrica (un palíndromo). Por ejemplo, "DABALEARROZALAZORRAELABAD" es un palíndromo.
- Crear un programa que encuentre el máximo común divisor de dos números usando el algoritmo de Euclides: Dados dos números enteros positivos m y n, tal que m > n, para encontrar su máximo común divisor, es decir, el mayor entero positivo que divide a ambos: - Dividir m por n para obtener el resto r (0 <= r < n) ; - Si r = 0, el MCD es n.; - Si no, el máximo común divisor es MCD(n,r).