3.2.- Las funciones Son muy parecidas a los procedimientos solo, que para estas la devolucion de un valor es sencillo. Aunque tambien con los procedimientos pueden devolverse valores aquellos no estan hechos para esto. Veamos un ejemplo: FUNCTION ELEVAR (MANTISA,EXPONENTE : BYTE) : REAL; BEGIN ELEVAR := EXP (LN (MANTISA) * EXPONENTE); END; Nuestra funcion es una manera util para elevar "x" a la potencia "y", esto es porque pascal carece de un operador de elevacion de potencias (el signo ^ lo usa para punteros). Observe que se mandan 2 parametros de tipo byte y que el tipo de elevar esta despues de los 2 puntos y el resultado se devuelve en el mismo nombre de la funcion. Tambien hay que observar que el resultado devuelto debe ser un tipo simple, esto quiere decir que no se pueden devolver registros o arrays, ademas cuando lo que se pretende devolver es un tipo string este debe ser el tipo string de 255 bytes, no podra ser un tipo string con longitud (debido a que ya no seria un tipo simple), puesto que no se acepta la notacion de corchete en ese lugar. Veamos como es esto: FUNCTION CADENA (NUMERO : BYTE) : STRING; {NO HUBIERA SIDO VA- } BEGIN {LIDO COLOCAR AQUI } : {STRING [50] U ALGUN } : {OTRO STRING DE VALOR} END; {CUALQUIERA. } 3.2.1.- Funciones predefinidas Funciones y Procedimientos principales: Abs ArcTan Chr Cos Dec Exit Exp Frac Halt Hi Inc Int IOResult Ln Lo Odd Ord Pi Pred Random Randomize Read ReadLn Round Sin SizeOf Sqr Sqrt Succ Swap Trunc Upcase Write WriteLn. La mayoria son sencillas por lo que explicaremos brevemente cada una de ellas; la (x) significa parametro que es el dato que se le pasa a la funcion o procedimiento: 3.2.1.1.- Funciones numericas generales Es una nutrida libreria que permite hacer cualquier manipulacion numerica lo cual hace del pascal un buen lenguaje para el tratamiento de numeros. La unica funcion que se extrania es probablemente es la funcion signo que en otros lenguajes esta desarrollada, pero es bastante sencilla de realizar, veamos : FUNCTION SGN (X : REAL) : INTEGER; BEGIN IF X < 0 THEN SGN := -1; IF X > 0 THEN SGN := 1 IF X = 0 THEN SGN := 0; END; Tambien hay que senialar que la funcion int y trunc devuelven ambos el mismo valor entero pero a causa del fuerte tipado de pascal no es facil la conversion de un tipo de dato a otro, por ello trunc devuelve la parte entera de un numero y lo deja como de tipo entero mientras que int devuelve la misma parte entera del numero pero lo deja como un real. Aqui la descripcion de las funciones: -Abs(x): Devuelve el valor absoluto de un numero real o entero siendo el tipo devuelto el mismo que se envio. -Dec(x,y): Decrementa la variable la cantidad "y". Ejemplo si tenemos A := 5; y hacemos dec (A,2) ahora "A" sera igual a 3. -Exp(x): Devuelve el valor de "E" elevado al parametro (ex). -Frac(x): Devuelve la parte fraccionaria de un real. -Inc(x,y): Devuelve la variable incrementada "y" veces. -Int(x): Devuelve un valor de tipo real conteniendo solo la parte entera del parametro. -Ln(x): Nos devuelve el logaritmo natural del parametro. -Odd(x): Devuelve un boolean, true si el numero es par, nos da false si el numero es impar. -Pi: Una funcion que devuelve el archiconocido valor de pi. -Round(x) : Redondea el parametro. Ejemplos: round (2.34) es 2, mientras que round (-2.6) es -3 etc. -Sqr(x): Eleva el parametro al cuadrado. -Sqrt(x): Le saca raiz cuadrada al parametro. -Trunc(x): Igual a int pero esta nos devuelve un tipo entero y tambien solo la parte entera del parametro 3.2.1.2.- Funciones Trigonometricas -Arctan(x): Devuelve el arcotangente de un angulo. -Cos(x): Devuelve el coseno de un angulo dado en radianes. -Sin(x): Devuelve el seno del angulo. Solo estan disponibles estas, pero con ellas es posible obtener todas las demas funciones veamoslas: secante (x) := 1/cos (x); cosecante (x) := 1/sin (x); tangente (x) ;= sin(x)/cos(x); cotangente (x) := 1/(sin(x)/cos(x)); arcseno (x) := arctan(x/sqrt(-x*x+1)); arccoseno (x) := arctan(x/sqrt(-x*x+1))+pi/2; arcsecante (x) := arctan(x/sqrt(x*x-1)); arccosecante (x) := arctan(x/sqrt(x*x-1))+(sgn(x)-1*pi/2); arccotangente (x) := arctan(x)+pi/2; senohip (x) := (exp(x)-exp(-x))/2; cosenohip (x) := exp(-x)/(exp(x)+exp(-x))*2+1; secantehip (x) := 2/(exp(x)+exp(-x)); cosecantehip (x) := 2/(exp(x)-exp(-x)); cotangentehip (x) := exp(-x)/(exp(x))-exp(-x))*2+1; arcsenhip (x) := ln(x+sqrt(x*x+1)); arccoship (x) := ln(x+sqrt(x*x-1)); arctanhip (x) := ln((1+x)/(1-x))/2; arcsechip (x) := ln((sqrt(-x*x+1)+1/x); arccosechip (x) := ln((sgn(x)*sqr(x*x+1/x); arccothip (x) := ln((x+1)/(x-1))/2; 3.2.1.3.- Funciones sobre los ordinales -Chr(x): Convierte un numero en el caracter correspondiente. Ejemplo: letra := chr (65); letra sera "A" mayuscula. (ver apendice caracteres ASCII). -Ord(x): La contrapartida de chr. Devuelve la posicion que ocupa el parametro dentro de un tipo ordinal. Por ejemplo ord('A') nos dara 65, ord(true) nos 1 (recuerde la enumeracion del tipo boolean) y si recuerda la enumeracion de semana cuando se hablo del tipo set; ord(jueves) nos dara 3. -Pred(x): Nos devuelve el elemento anterior en una enumeracion o en una secuencia de ordinales. Ejemplo: pred('b') nos dara el caracter "a" pred(true) nos dara false y pred(jueves) nos dara miercoles recuerde que los tipos enumerados no se pueden imprimir. -Succ(x): Igual a pred pero este nos devuelve el sucesor en la enumeracion. Hay que tener mucho cuidado con salirse de los limites en los pred y los succ. Por ejemplo de haber tratado de leer el anterior de false (que es el primer valor = 0) pred(false) nos daria un valor sin significado por ello hay que asegurarse que esto no ocurra. 3.2.1.4.- Funciones de entrada y salida Las unicas formas de comunicarse con el usuario en pascal standard son por medio de los read y write que son formas mas que rudimentarias para hacerlo debido a que con read no se tiene control sobre el espacio que ocupara el mensaje al ser introducido. Esto no era un trastorno en el tiempo en que se desarrollo el pascal, despues de todo lo unico que poseian nuestros abnegados antepasados eran las impresoras y todos los trabajos eran realizados por lotes, no habia interaccion con el usuario. Pero el mundo informatico a cambiado, ahora la entrada se hace por consola y ademas es interactivo por lo que cada vez se usan menos los procedimientos que vamos a ver, los cuales se utilizan ahora casi totalmente para el acceso al disco, pero tampoco es para despreciarlos del todo porque tienen algunas ventajas muy grandes estas son: -Son polimorficos: Osea que aceptan cualquier tipo de dato y lo procesan de diferente manera. -Son redireccionables: Osea que se puede utilizar la capacidad de redireccion del dos para leer datos de un fichero, y alimentar las sentencias read del programa, mezclar ficheros etc. Veremos esto mejor mas adelante. -Read(x,y,z...): Acepta entradas del usuario seguidas. -Readln: Lee pero baja una linea a continuacion. Nunca se admiten entradas de tipo boolean. -Write(x): El parametro puede ser cualquier tipo menos una enumeracion y se imprime en la posicion del cursor. -Writeln(x): Igual que write pero se baja a la siguiente linea despues de la impresion. -Ioresult: Nos devuelve el estado de la ultima operacion de entrada y salida. Por ejemplo en el programa declaramos una variable "a" de tipo integer y pedimos que se ingrese un dato con READLN (A); y el usuario nos responde con un caracter en vez de un entero, el programa morira a causa de un fallo de E/S, a no ser que entorno del readln pongamos la directiva de desactivacion a activacion de entrada y salida, veamos: {$I-} READLN (A); {$I+} Ahora podemos preguntarle a ioresult, como fue la operacion, IF OIRESULT = 0 THEN WRITELN ('OPERACI N CORRECTA'); Un valor de ioresult diferente de cero indica una falla en la operacion de entrada y salida pero no hara que el programa aborte con un runtime error... (esto es importante) por eso su uso es generalizado en operaciones del disco (en el capitulo de operacion con el disco veremos esto un poco mejor). Observe que el valor de ioresult cambia cada vez que existe una operacion de E/S por eso no puede imprimirlo directamente con writeln (debido a que writeln es una operacion de E/S) asi que si lo necesita puede guardarlo en una variable y luego imprimirla, trabajar con ella etc, ademas se recomienda volver a activar los errores de E/S (con {$I+}) para que nosotros podamos advertir de cualquier otro error de E/S. 3.2.1.5.- Funciones de ruptura de codigo -Exit: Permite que salgamos del bloque actual a un nivel inferior de un bloque de sentencias. Se usa cuando se puede perder el control en los anidamientos o cuando estos se vuelven complicados y dificiles de seguir. Un exit dentro de un procedimiento nos dejara en el programa principal mientras que lo mismo en el programa principal hara que salgamos al sistema operativo. -Halt(x): Hace que termine el programa desde donde se encuentre. Este debe ser el ultimo recurso cuando no se pueden controlar los anidamientos o la salida normal es demasiado compleja o en el peor de los casos ocurrio un error critico del tipo "desastre en el ordenador". El parametro que se le da es un numero que se pasa a la seudovariable del sistema operativo llamada errorlevel y puede ser comprobada normalmente mediante un fichero con extension bat del dos, mas adelante existe un ejemplo de como hacer esto. 3.2.1.6.- Funciones sobre numeros aleatorios -Randomize: Inicializa el gestor de numeros aleatorios (llamado comunmente, semilla) para que se generen numeros que parezcan verdaderamente aleatorios. De no establecer la inicializacion con randomize los numeros se repetiran sistematicamente cada vez que el programa se ejecute. -Random(x): Nos dara un numero entero aleatorio de tipo word comprendido entre 0 y parametro-1. Asi para simular un dado sera necesario escribir dado:=random(6)+1; y esto nos dara un numero entero de 1..6 (debido a que random(6) dara un valor entre 0..5). Tenga presente que los numeros asi formados no son verdaderamente aleatorios en teoria, pero para la practica si lo seran. -Random: La segunda forma de random sin parametros nos da un numero real fraccionario en el intervalo de <0..1>, sin incluir los limites. 3.2.1.7.- Otras funciones -Hi(x): Devuelve la parte alta en forma de byte de un numero. Recordando que si ponemos un signo dolar adelante hablamos de un numero en hexadecimal, si hacemos a:=hi($1234); tendremos en a:= $12 que en base decimal es 18. -Lo(x): Lo mismo que hi pero nos devuelve el byte menos significativo. Ejemplo lo($1234) nos dara $34 que es 52 en base decimal. -Sizeof(x): Nos devuelve el numero de bytes del tipo al que pertenece el parametro. Ejemplo, declarando: VAR A : LONGINT; { EL TIPO LONGINT ES DE 4 BYTES } BEGIN WRITELN (SIZEOF (A)); { AQUI IMPRIME 4 } -Swap(x): Devuelve el mismo tipo del parametro e intercambia el byte bajo con el alto. Por ejemplo haciendo swap($3456) nos dara $5634 (recordando que $ indica numero hexadecimal). -Upcase(x): Nos devuelve la mayuscula del caracter mandado como parametro. Upcase('a') nos dara la letra mayuscula "A". Esta funcion se usa mucho para pedir al usuario una contestacion, entonces el valor ingresado se le convierte a mayusculas y luego se compara, asi nos liberamos de algo de codigo. Por ejemplo: A := UPCASE (READKEY); IF A = 'S' THEN WRITELN ('RESPUESTA = SI'); Asi nos liberamos de comprobar las minusculas. Aunque la funcion coseno ya existe veamos un programa que calcula el coseno de un angulo, para mejorar nuestro conocimiento sobre funciones y ver como se opera con los numeros en pascal (no se preocupe por las funciones nuevas, se explicaran). PROGRAM COSENO; { ------------------------------------------------------- } { - PROGRAMA QUE DEMUESTRA EL USO DE FUNCIONES - } { ------------------------------------------------------- } {- CALCULA EL COSENO DE UN ANGULO -} {- DADO EN RADIANES POR LA FORMULA -} {- SIGUIENTE : -} {- DONDE Z ES EL ANGULO -} {- K 2K EXPRESADO EN RADIANES -} {- N (-1) * (Z) -} {- ä --------------- -} {- K=0 (2K)! -} {---------------------------------------------------------} USES CRT; VAR X,Y,VALOR,DIFERENCIA,Z : REAL; FUNCTION COSINE (ANGULO : REAL) : REAL; VAR K,D,M,P : INTEGER; S,POTENCIA,F : REAL; BEGIN S := 0; FOR K := 0 TO 6 + TRUNC (Z) DO { SE AniADE TRUNC PARA HACER } BEGIN { MAS UNIFORME EL CALCULO } F := 1.0; { EN VARIAS CORRIDAS. } FOR P := 1 TO 2 * K DO F := F * P; { AQUI ESTA (2K)! } IF (K MOD 2) = 0 THEN D := 1 ELSE D := -1; { AQUI (-1) ^ K } POTENCIA := EXP (LN (ANGULO) * (2 * K)); { AQUI Z ^ 2K } S := S + (D * POTENCIA) / F; { ACUMULA LA SUMA } END; COSINE := S; { AQUI SE DEVUELVE EL VALOR AL PROGRAMA } END; { AQUI COMIENZA EL PROGRAMA PRINCIPAL } BEGIN CLRSCR; WRITELN ('INGRESE UN ANGULO EN RADIANES'); READLN (Y); Z := Y - (TRUNC (Y / (2 * PI)) * (2 * PI)); { ELIMINA MULTIPLOS DE 2 * pi} WRITELN; WRITELN ('NUESTRA FUNCION DEVUELVE EL VALOR '); WRITELN (COSINE (Z),' RADIANES'); WRITELN; WRITELN ('LA Standard NOS DEVUELVE ',COS (Z)); WRITELN; DIFERENCIA := ABS (COSINE (Z) - COS (Z)); WRITELN ('EXISTE UNA PEQUENA DIFERENCIA QUE ES ',DIFERENCIA); END. El programa principal es bastante sencillo. Solo nos pide un angulo en radianes, le quita un multiplo de 2*ã y luego pide a la funcion que calcule el valor del angulo y lo compara con el que devuelve la funcion standard. Lo que hace falta es un analisis de la funcion en si. En primer lugar olvide el nombre con que se llama a la funcion, vea solo que el parametro se llama angulo y que tenemos alli un angulo dado en radianes. Al interior se inicializa la variable suma (sino el programa correria solo en el entorno turbo pascal). Se tiene un for. En general las sumatorias se gestionan con los for y una variable que acumule la suma, por supuesto que esta se tiene que inicializar fuera de este. Luego el programa calcula lo que necesita por partes (esto es la base de una programacion eficiente), en la linea siguiente tenemos f := 1.0 (vamos a acumular el factorial asi que inicialmente debe ser 1) y luego un for que hace que se acumule lo multiplicado en una variable, recuerde que f := f * p hace que en f quede el mismo valor antiguo multiplicado por p. La sentencia que utiliza el operador mod dice: si el residuo de (k / 2) es cero (esto indica que el numero es par) entonces el signo (d) es + (1), caso contrario negativo (-1), lo mismo hubiera sido poner: IF ODD (K) THEN D := 1 ELSE D := -1; debido a que odd(k) da true si el numero es par. Lo que sigue es nuestra conocida formula de elevacion a potencias pero con otro ropaje, y por ultimo viene el acumulador de sumas sucesivas del que hablamos antes, luego se le asigna al nombre de la funcion a lo calculado y retornamos con el valor de dicho calculo en cosine. NOTA: Usamos la variable "s" dentro del for porque si hubiesemos querido acumular el resultado en el nombre de la misma funcion turbo pascal hubiera creido que se trata de otra llamada a la funcion (pues esta al lado derecho del igual) y no nos hubiera dejado hacer esto porque no se trata de una llamada recursiva. .