UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. 18. PROCEDIMIENTOS Y FUNCIONES 18.1. Razones de Uso. Los procedimientos y funciones tienen un papel fundamental en el diseño de programas. Debido a su importancia existen distintas razones para su utilización, las que se describirán a continuación. 18.1.1. Estructura del algoritmo. Como se vió en la introducción del concepto en forma informal, los procedimientos permiten describir una tarea en términos de subtareas, destacando la estructura dominante y suprimiendo los detalles. Permitiendo de esta forma el desarrollo jerárquico (top-down) de programas, y también facilitando un lenguaje apto para el método de desarrollo por refinaciones sucesivas. Los nombres que se emplean en el programa principal, y que representan subtareas, se conocen como llamadas o invocaciones al procedimiento. Sintácticamente un identificador de procedimiento es una instrucción (o statement), y la acción que se realiza es la ejecución del bloque asociado al nombre mediante la declaración del procedimiento. Una vez ejecutado el bloque del procedimiento, se retorna a la instrucción que sigue a la que produjo la ejecución del bloque. La declaración del procedimiento define las acciones que serán realizadas; es en esta parte en la que se especifican los detalles que se abstraen con el nombre que identifica el procedimiento. Los procedimientos permiten dividir y estructurar un programa en componentes lógicamente coherentes; facilitando así la verificación (prueba por etapas); mejorando la documentación y entendimiento del programa. El estilo y calidad de un programa depende en gran medida de la adecuada elección de los procedimientos a emplear. Una forma de aprender eficientemente la técnica de "dividir para vencer", escogiendo procedimientos simples y poderosos, se logra mediante el análisis de programas (ojalá complejos) escritos por buenos programadores. Otro aspecto, que ayuda, es un proceso reflexivo, casi meditativo, respecto del problema: es decir, pensar en el problema. También ayuda el trabajo previo con papel y lápiz, y no dudar en volver a reescribir varias veces el programa principal y algunas descripciones de procedimientos secundarios. Prof. Leopoldo Silva Bijit. 07-07-2003 217 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. En largos programas y algoritmos complejos es particularmente útil el concepto de procedimiento. En casos simples y programas cortos, pueden reemplazarse los procedimientos por las acciones que los representan; sin embargo, si se desea tener claridad en la estructura del programa, es recomendable usar procedimientos explícitos, aún en casos simples. 18.1.2. Reducción de espacio. Si el mismo procedimiento será invocado en diversas partes del programa, su uso implica reducir el largo del programa. Lo cual lleva asociado una disminución del tiempo de edición del texto, ya que no es necesario copiar repetidas veces las secuencias de instrucciones que se repiten. Este uso ve aumentada su potencialidad con el concepto de parámetros, que se verá más adelante, y que hace posible invocar el mismo procedimiento, en diferentes puntos de un programa, pasándole al procedimiento diferentes valores y variables determinados en el punto de invocación. Si el largo del texto es menor, la posibilidad de cometer errores es menor. Además una reducción del texto lleva a una reducción del tamaño del código compilado, produciendo ahorro de memoria. También el tiempo de compilación es menor. 18.1.3. Facilidad de verificación y pruebas. En Pascal, la sintaxis de declaración de procedimientos es similar a la de un programa. En este sentido es fácil editar un procedimiento como un programa, y luego de probarlo, incorporarlo, a través del editor de textos, al programa. Algunos compiladores ofrecen la posibilidad de compilación separada de procedimientos; en estos casos, se logran disminuciones notables en el tiempo de desarrollo de un programa. 18.1.4. Acciones especiales. Dado un ambiente de programación, por la estructura de datos que se emplee, pueden conceptuarse acciones especiales (más elaboradas que las del lenguaje de alto nivel) para ese ambiente. Es decir procedimientos que manipulen las estructuras a través de operaciones. Se logra entonces, especializar el repertorio de instrucciones para el ambiente dado. Luego con este Macrolenguaje puede efectuarse la programación en forma más eficiente. Lo anterior está relacionado con la técnica de programación conocida como máquinas virtuales; consistente en recubrir las primitivas instrucciones de máquina por diferentes capas de Prof. Leopoldo Silva Bijit. 07-07-2003 218 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. software (programas) que permiten al programador ver una máquina especializada con las instrucciones más adecuadas para su aplicación. 18.2. Sintaxis de Procedimientos. Parámetros. <invocación> ::= <identificador de procedimiento> [<lista parámetros actuales>] <declaración> ::= 'PROCEDURE' <identificador> [<lista parámetros formales>] ';' <bloque> ';' En un ambiente de programación se dice que un parámetro es un mecanismo de substitución que permite repetir un proceso con una variación de sus argumentos. Los parámetros describen la comunicación entre un procedimiento y su ambiente. La lista de parámetros del programa principal también establece la comunicación entre el ambiente externo, teclado, consola y archivos, y el programa. De esta forma los parámetros equivalen a las instrucciones de entrada y salida (read y write) del programa principal. La sintaxis de lenguajes actuales de alto nivel, como el Ada, califican los parámetros formales como de entrada, salida y entrada/salida; haciendo énfasis en el aspecto de comunicación. En Pascal, en forma más tradicional, se habla de paso de parámetros y retorno de éstos. Una práctica recomendable es que la comunicación de un procedimiento con su ambiente sea sólo a través de los parámetros; evitando la comunicación mediante asignaciones a variables globales dentro de procedimientos. Esto último se conoce como efectos laterales y es equivalente a tener salidas secundarias; quebrantando la regla de programación estructurada de que cada acción tenga sólo una entrada y una salida. Prof. Leopoldo Silva Bijit. 07-07-2003 219 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. En Pascal existen cuatro tipos de parámetros: parámetros de valor, parámetros variables, parámetros función y parámetros procedimientos. En este curso introductorio sólo se desarrollarán los dos primeros; considerándose demasiado especializados los dos últimos. Con estas consideraciones: <lista parámetros formales> ::= '(' {( ['var'] {<identificador>* ','} ':' <tipo>)* ';' } ')' <lista parámetros actuales> ::= '(' {( <expresión>)* ',' } ')' Nótese que los parámetros formales se separan con un punto y coma y lo s actuales por comas. También hay que notar que los formales deben especificar el tipo. Para cada parámetro formal debe existir uno actual asociado, éste último debe tener igual tipo que el formal correspondiente. La correspondencia se establece por las posiciones de los parámetros en las listas. Esto permite al compilador verificar si se ha suministrado un parámetro actual apropiado y si las listas son compatibles. Conviene dibujar los grafos sintácticos de las producciones anteriores. Los parámetros variables van precedidos por la palabra reservada var en la lista de formales; y deben tener asociado una variable (caso más simple de expresión) en la lista de actuales, y cuyo tipo debe ser igual al declarado para la formal. Los parámetros valor, no van precedidos por var, en la lista de formales; y deben tener asociado una expresión (en el caso más simple una variable o constante) en la lista de actuales, y el tipo de la expresión debe ser compatible con el tipo declarado para el formal. Todos los identificadores que aparecen en la lista de parámetros formales son locales al procedimiento; así también todos los objetos declarados o definidos en el bloque correspondiente. Es decir, ellos no son conocidos fuera del bloque. Específicamente las variables locales tienen indefinidos sus valores al iniciar la parte ejecutable del bloque. Es importante notar que en la lista de parámetros formales debe aparecer un nombre de un tipo. En Pascal estándar no puede efectuarse una definición de tipo en la lista de parámetros. Prof. Leopoldo Silva Bijit. 07-07-2003 220 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. Versiones recientes permiten definir tipos en la lista de parámetros; facilitando así el tratamiento de arreglos. Prof. Leopoldo Silva Bijit. 07-07-2003 221 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. 18.3. Tipos de Procedimientos. 18.3.1. Sin parámetros. Sólo acción. En estos procedimientos no se suministran datos, desde el ambiente hacia el procedimiento; ni se esperan resultados, desde el procedimiento hacia el punto de invocación. Se emplean sólo para ejecutar una serie de instrucciones al invocar o llamar al identificador que da nombre al procedimiento. Ejemplo: Procedimientos sin parámetros. Limpiar la pantalla, dejándola en blanco. procedure limpiepantalla; var i:integer; begin for i:=1 to 24 do writeln end; Al invocar a limpiepantalla, se desplazan (scroll en inglés) las líneas, quedando el cursor en el borde inferior izquierdo; no importando la posición inicial del cursor. Nótese que la variable i es local al procedimiento, y existe mientras se ejecuta éste. Se emplean procedimientos sin parámetros cuando se desea realizar una acción compleja, bajo un nombre abstracto que las represente. El bloque del procedimiento debe quedar completamente especificado y no se espera realizar variaciones de la acción. Algunos programadores emplean estos procedimientos, y se comunican con otros bloques a través de variables globales. Esto debe evitarse, pues dificulta el análisis y las pruebas del programa. Una variante del ejemplo anterior es: procedure clearscreen; const ff = 12; begin write(chr(ff)) end; Prof. Leopoldo Silva Bijit. 07-07-2003 222 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. Esta acción deja el cursor en el borde superior izquierdo. Sin embargo debe probarse en la práctica, pues algunos terminales no reconocen dicho carácter de formato. En este caso debe consultarse el manual técnico del terminal que se esté empleando. 18.3.2. Parámetros de valor. Sólo entrada. Se pasan valores desde el punto de invocación hacia el procedimiento. Es decir, sólo se suministran datos de entrada y no se esperan resultados. El bloque de accio nes, asociado al procedimiento, depende de los valores que se le pasen. El parámetro formal es una variable local. El parámetro actual es una expresión. Se evalúa, el parámetro actual, y el valor de la expresión sustituye el correspondiente parámetro formal. Es decir, se asigna inicialmente el valor a la variable local o parámetro formal. A este tipo de procedimientos se los denomina sustitución por valor, o llamado por valor, o paso de parámetros valor. Una vez realizado el procedimiento, se retorna a la instrucción siguiente a la que produjo la invocación, sin producir cambios en este ambiente; salvo que dentro del bloque se hayan modificado variables accesibles en el ambiente y en el bloque. Esta última práctica es posible, pero no es recomendable. Ejemplo. Procedimiento con parámetro valor. Se desea lograr un margen variable a la izquierda. procedure margen(x:integer); const blanco = ' '; var i:integer; begin for i:=1 to x do write(blanco) end; El parámetro formal es un parámetro valor y corresponde a la variable local x. La variable i es local, sólo existe mientras dure el procedimiento. La constante blanco también es local. Nótese que el tipo queda definido en la declaración; es decir, x es de tipo integer. Prof. Leopoldo Silva Bijit. 07-07-2003 223 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. Dentro de un segmento, donde el procedimiento margen es accesible, puede invocarse a margen con diferentes valores: . . m:=5; {m debe ser entero} margen(m); {se llama con variable; produce 5 espacios} . . margen(3); {se llama con constante; produce 3 espacios} . . margen(m+3); {se llama con expresión; produce 8 espacios} . . x:=6; {x debe ser entero} margen(x); {se pasa valor 6} {no se modifica valor de x; este x no tiene que ver} {con la variable local del procedimiento margen} . . Cuando se invoca a un procedimiento de este tipo, se crea espacio para las variables locales, incluídas las variables formales. Luego, estas últimas se inician con los valores calculados de las expresiones asociadas a los parámetros actuales. Dentro del bloque, no quedan iniciadas las variables estrictamente locales, lo cual es tarea del programador; también dentro del bloque pueden modificarse, mediante manipulaciones, todas las variables accesibles. Una vez terminado el bloque, desaparece el espacio asignado a todas las variables. Los parámetros formales tienen igual comportamiento que las variables locales, excepto que comienzan con valores asignados al iniciar el bloque del procedimiento. Si un parámetro valor es de tipo estructurado, deben copiarse los valores de las expresiones en los elementos de la estructura; esto puede consumir tiempo y espacio. Si en las expresiones ocurren variables estructuradas, debe accesarse a la estructura (en el caso de un arreglo, debe evaluarse el índice) y obtener el valor. Una técnica que es general para todo tipo de procedimientos es detectar todos los objetos que no se usan fuera del procedimiento, y declararlos locales a éste. Prof. Leopoldo Silva Bijit. 07-07-2003 224 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. En general, los parámetros valor son los más usados. 18.3.3. Parámetros variables. Entrada y Salida. Este tipo de procedimiento permite transferir información desde el punto de invocación hacia el procedimiento; y el retorno de resultados desde el procedimiento. Los resultados los entrega modificando variables visibles o accesibles en el punto de invocación, que se pasan al procedimiento. Es en el parámetro actual, en el que se retorna el resultado del procedimiento. Los parámetros formales variables van precedidos por var en su declaración. También se llaman sustitución por variable, o llamado por referencia. Cuando se invoca a un procedimiento de este tipo, no se crea nuevo espacio para el parámetro formal; sino que se hace referencia a la variable actual. Es decir, el parámetro formal representa a la variable actual, durante la ejecución del procedimiento. El parámetro formal es realmente una variable muda. Si las variables son estructuradas, todos los accesos se realizan en el tiempo de la invocación. Si una variable es una componente de un arreglo se evalúa el índice cuando se invoca al procedimiento. Al no crear espacio adicional para los parámetros formales, no hay pérdida de tiempo en la copia y accesos. Por esta razón es preferible definir parámetros variables, cuando éstos sean estructurados; existe ahorro de espacio y tiempo. Adicionalmente, en Pascal estándar se impide emplear estructuras empaquetadas como parámetros variables. Si existen varios parámetros variables, debe evitarse, en el momento de la invocación, que existan nombres duplicados de variables en la lista. Es decir, la lista de parámetros actuales debe ser disjunta. Ejemplo. Procedimiento con parámetros variables. Se desea descartar espacios y leer un carácter. procedure getchr(var ch:char); begin repeat read(ch) until ch<>' ' Prof. Leopoldo Silva Bijit. 07-07-2003 225 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. end; Se tiene que ch es un parámetro formal variable, de tipo char. En un segmento, en que sea accesible getchr, pueden asignarse valores a variables de tipo char, que sean visibles en el momento de la invocación. . . getchr(a); {se asigna valor a variable a} . . getchr(b); {se asigna valor a b} . . El ejemplo ilustra el uso de un parámetro variable para depositar el resultado. O sea se usa sólo de salida. También pueden emplearse para pasar una variable (con su valor) como entrada, y luego de procesar sacar el resultado. En este caso se usa el valor viejo de la variable actual, antes de cambiarlo. Esta subclase es realmente de entrada/salida. Ejemplo. Procedimiento con entrada/salida. Se desea incrementar variables enteras que funcionan como contadores. procedure incremente(var n:integer); begin n:=n+1 end; Si v1 y v2 son contadores enteros: . . incremente(v1); . . incremente(v2); . . Prof. Leopoldo Silva Bijit. 07-07-2003 226 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. Como observación final se destaca que un parámetro actual no puede ser una expresión o una constante; debe ser una variable. Se recomienda usar este tipo de procedimientos para manipular estructuras de datos; por ejemplo: arreglos y archivos. Prof. Leopoldo Silva Bijit. 07-07-2003 227 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. 18.3.4. Mezcla de parámetros. Los puntos anteriores, con objeto de aclarar conceptos, han usado sólo un parámetro de determinado tipo. Sin embargo, en un caso general, pueden existir parámetros de diferente tipo en una lista. Ejemplo. Paso por valor y por referencia. program mezcla(output); var a,b:integer; {globales} procedure h(x:integer; var y:integer);{x par. valor} begin {y par. variable} x:=x+1; y:=y+1; {p1} writeln(x,y) {p2} end; {h} begin a:=0; b:=0; h(a,b); writeln(a,b) end. {mezcla} {1} {2} {se pasa valor cero y var b} {3} Traza: # a b Observaciones. -------------------------------------------------------1 0 0 Se inician a y b. 2 y x Se entra a proc. h. -----------------------------------------0 0 Se inicia x con valor de a y toma lugar de b. p1 1 1 Se incrementa x e y. p2 Se escribe: 1 1 -----------------------------------------1 Al salir, desaparece x. b toma el valor de y. La variable a, no cambia. 3 Se escribe: 0 1 --------------------------------------------------------- Prof. Leopoldo Silva Bijit. 07-07-2003 228 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. Si en el paso 2, se hubiera invocado: h(b, a) puede comprobarse que se escribe: 11 10 En este caso al salir del procedimiento, se modifica el valor de a; y b no cambia su valor. Nótese que en la traza, se crea espacio sólo para parámetros formales de valor. Los parámetros variables ocupan igual columna que las variables que reemplazan. 18.4. Procedimientos Estándares. Se asumen declarados en un bloque que anida al programa principal. Y por lo tanto son accesibles en cualquier lugar del programa. También puede advertirse, que si se emplea un procedimiento de igual nombre que uno estándar; éste tendrá vigencia en el bloque que define su alcance, de acuerdo a la regla de alcance de nombres. Esto permite redefinir las acciones de los procedimientos estándar. Las instrucciones primitivas: read, write, writeln y readln, son efectivamente procedimientos en Pascal. Se las ha introducido como instrucciones básicas, por las ventajas conceptuales que esto presenta. Debe notarse que son procedimientos especiales, ya que aceptan un número variable de parámetros; y además se pueden pasar parámetros de distintos tipos. 18.5. Funciones. 18.5.1. Definiciones. Conceptualmente los procedimientos son una abstracción de acciones especiales o complejas. Una función es una abstracción de una expresión compleja. Su utilización es muy similar a la matemática; en la que se emplea una función para calcular un valor a partir de sus argumentos. Prof. Leopoldo Silva Bijit. 07-07-2003 229 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. La función permite emplear un resultado computado, como un paso intermedio, en una expresión. Ejemplo: x:=sin(y)*2 Se invoca a la función estándar seno, con argumento y; el valor calculado, se multiplica por 2 y se asigna a x. El identificador de una función representa el bloque de acciones asociado, así como también el resultado computado. La declaración de función se caracteriza por tener una indicación del tipo del resultado, después de la lista de parámetros. Es decir queda asociado un tipo al nombre de la función. En Pascal, el tipo de la función no puede ser estructurado. Se aceptan los primitivos estándares y los escalares definidos (enumeración y subrango). Es importante destacar que una función sólo retorna un valor como resultado. Si se desea pasar más valores debe hacerse a través de parámetros variables, o asignaciones a variables no locales al bloque de la función (lo que se denomina efectos laterales); en estos casos es preferible desarrollar un procedimiento en lugar de una función. 18.5.2. Sintaxis. <invocación> ::= <nombre de función> '(' { <expresión>* ',' } ')' <declaración de función> :: = 'FUNCTION' <nombre><lista parámetros formales>':'<tip o>';' <bloque> ';' La invocación es una posibilidad de factor dentro de la sintaxis general de expresiones. La lista de parámetros formales será similar a la vista en procedimientos. No se verán parámetros función y parámetros procedimientos. Prof. Leopoldo Silva Bijit. 07-07-2003 230 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. En general se recomienda emplear parámetros valor; y restringir el uso de parámetros variables a pasar datos estructurados de entrada solamente. El empleo de parámetros variables, que sean modificados en el cuerpo de la función, genera salidas secundarias, lo cual dificulta el manejo conceptual de una función como un objeto asociado a un valor computado. Una característica del bloque asociado a la función es que debe contener, por lo menos una asignación al nombre de la función. Este es precisamente el valor retornado. Es responsabilidad del programador la iniciación de las variables locales. Los parámetros formales por valor, son iniciados por los correspondientes valores de las expresiones (o parámetros actuales). Los parámetros variables también tienen sus valores iniciales dados por las variables actuales correspondientes. 18.5.3. Ejemplos de funciones. a) Elevar al cuadrado un entero. Parámetro valor. function cuadrado(x:integer):integer; begin cuadrado:=x*x {se retorna resultado} end; Puede invocarse a cuadrado en expresiones: y := cuadrado(5); y := y+cuadrado(y); {y debe ser variable entera} a[i] := cuadrado(y+5)*a[i]; y := cuadrado(a[i]); {se evalúa i; luego el valor de la componente} b) Función con espacio local. function sqrt(x:real):real; const eps=1.0e-8; var x1, x0:real; {espacio local} begin {método de Newton para x>1} x1:=x; Prof. Leopoldo Silva Bijit. 07-07-2003 231 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. repeat x0:=x1; x1:=(x0+x/x0)*0.5 until abs(x1-x0)<eps*x1; sqrt:=x1 {retorno} end; También puede retornarse el valor x0, pero es mejor aproximación el valor de x1. Para deducir el algoritmo puede aplicarse el método de Newton a la función: f(x) = x ^ 2 - a = 0. Para valores del argumento entre 0 y 1 se suele emplear el siguiente algoritmo: function sqrt(x:real):real; {0<x<1} const eps=1.0e-5; var x1,x0:real; begin x0:=x; x1:=1-x; while abs(x1)>eps do begin x0:=x0*(1+0.5*x1); x1:=sqr(x1)*(0.75+0.25*x1) end; sqrt:=x0 {retorno} end; Como ejercicio puede desarrollarse una función que trate todo el rango. Con tratamiento especial para 0 y 1; además puede protegerse contra argumentos negativos. c) Sumar los primeros n enteros. Se desea obtener: j =n sigma(n)= ∑j j =1 Podría plantearse: Prof. Leopoldo Silva Bijit. 07-07-2003 232 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. function sigma(n:integer):integer; var j:integer; begin sigma:=0; for j:=1 to n do sigma:=sigma+j {error} end; Pero no puede usarse un nombre de función en una expresión. Al nombre de la función sólo puede asignarse un valor, y debe figurar a la izquierda del signo de asignación. Como veremos más adelante dentro de una expresión puede usarse una invocación a la misma función que se está definiendo; esto se denomina recursividad. En forma correcta, debe usarse una variable local suma: function sigma(n:integer):integer; var j, suma:integer; begin suma:=0; for j:=1 to n do suma:=suma+j; sigma:=suma {retorna el valor} end; Puede considerarse: sigma(n)=1+ 2 + 3 +........+(n-2)+(n-1)+n sigma(n)=n+(n-1)+(n-2)+........+ 3 + 2 +1 Las dos expresiones, a la izquierda, son equivalentes; sólo se ha cambiado el orden. Sumando las dos ecuaciones, puede comprobarse que: sigma=n(n+1)/2 Entonces: function sigma(n:integer):integer; begin sigma:=(n*(n+1)) div 2 end; Prof. Leopoldo Silva Bijit. 07-07-2003 233 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. Obsérvese que se asegura efectuar primero el producto, que será par; y por lo tanto divisible por dos. d) Sobre efectos laterales. En general consisten en asignar valores a variables que no son locales al bloque o cuerpo de la función. Existen raras excepciones en que se desea este efecto. Una situación típica es cuando se desea contar el número de veces que se efectúa una función. Por ejemplo, en el caso de la función cuadrado vista en el caso a), se desea como resultado primario obtener el valor del cuadrado; y como valor secundario el número de veces que se ha invocado a esta función Sea ncuad la variable en que se almacenará la cuenta, entonces: function cuadrado(x:integer):integer; begin ncuad:=ncuad+1; cuadrado:=x*x end; En este caso, ncuad no puede ser local; si lo fuera, desaparecerá al salir de la función. Se sobreentiende cual será el uso de ncuad, fuera de la función. Pero consideremos ahora, en forma hipotética, que en segmentos donde son accesibles ncuad y la función cuadrado se construyeran las expresiones: i) cuadrado(y)+ncuad ii) ncuad+cuadrado(y) Puede comprobarse que las sumas anteriores no serán iguales; violando la ley de conmutatividad de la suma, aún cuando no se exceda el máximo entero. Estos efectos pueden producir resultados inesperados cuando la función, que los contiene, es usada inadecuadamente. Prof. Leopoldo Silva Bijit. 07-07-2003 234 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. e) Diseño de funciones especiales. Asumiendo que se dispone de un tipo vector: type vector=array [1..nmax] of real; Pueden desarrollarse funciones especiales para manipular variables que tengan la estructura anterior. Además se intenta demostrar como se pueden manejar arreglos de largo variable; esto es posible si el número variable de componentes se mantiene menor que nmax. Es decir habrá un espacio reservado que no se utilizará. e1) Función que retorne el máximo valor en un vector de n componentes; con n<=nmax. function max(a:vector; n:integer):real; var x:real; i:integer; begin x:=a[1]; for i:=2 to n do if x<a[i] then x:=a[i]; max:=x {retorno} end; En general, obtiene el máximo valor entre las primeras n componentes del vector. Nótese que se pasan parámetros de valor; y adicionalmente no se efectúa asignaciones a las componentes del vector. Esto permite diseñar: function max(var a:vector; n:integer):real; var x:real; i:integer; begin x:=a[1]; for i:=2 to n do if x<a[i] then x:=a[i]; max:=x end; La ventaja de esta implementación, es que mientras está activa la función se ocupa menos espacio de memoria; ya que no se crea espacio local, como en la primera implementación. También es más rápida ya que no hay necesidad de copiar los valores iniciales en el espacio local. La economía de espacio y tiempo puede ser significativa si nmax es elevado. Prof. Leopoldo Silva Bijit. 07-07-2003 235 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. e2) Función que retorna la suma de las n primeras componentes. function sum(var a:vector; n:integer):real; var i:integer; s:real; begin s:=0.0; for i:=1 to n do s:=s+a[i]; sum:=s {retorno} end; 18.5.4. Funciones estándares. Cuando se estudió tipos de datos, en la parte correspondiente a manipulación, se describieron las funciones estándar. Es un buen ejercicio escribir los encabezados de las funciones estándar. Como ejemplos: function round(x:real):integer; function odd(i:integer):boolean; function eoln(t:text):boolean; Lo cual facilita reconocer el ambiente en que se aplica la función. Lo que puede observarse en la lista de parámetros formales y en el tipo del resultado. 18.6. Ejercicios. 18.6.1. Dado el siguiente programa: a) Redefinir variables tal que faciliten el análisis. (Pero conservando exactamente el significado del algoritmo.) Prof. Leopoldo Silva Bijit. 07-07-2003 236 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. b) Determinar lo que escribe. program parametros; var i,j,k:integer; procedure wrtglb; begin writeln('globales :i,j,k',i:4,j:4,k:4) end; procedure p(var i:integer); begin writeln('al entrar a p:i,j,k',i:4,j:4,k:4); i:=i+1; writeln('al salir de p:i,j,k',i:4,j:4,k:4) end; procedure q(h:integer; var j:integer); var i:integer; procedure r; begin i:=i+1 end; begin writeln('al entrar a q:i,j,k',i:4,j:4,k:4); i:=j; if h=0 then p(j) else if h=1 then p(i) else r; writeln('al salir de q:i,j,k',i:4,j:4,k:4) end; begin i:=0;j:=1;k:=2; wrtglb; q(0,k); wrtglb; q(1,i); wrtglb; q(2,j); wrtglb end. 18.6.2. Limpia la pantalla. El llamado : write(clear); Prof. Leopoldo Silva Bijit. 07-07-2003 237 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. {limpia la pantalla y toca la campanilla} function clear : char; var i : integer; begin for i:=1 to 24 do writeln; clear:=chr(07) end; 18.6.3. Calendario. Determinar una función que retorne el número de días de un mes, dado el mes y el año. Deben considerarse los bisiestos. 18.7. Ejemplos. 18.7.1. Determinar qué escribe el programa. Explicar qué principio de la programación estructurada no cumple el programa test. program test(output); var a,b : integer; function h(x:integer); integer; begin b:b-x; h:=sqr(x) end; begin b:=10; a:=h(b); writeln(a,b); b:=10; a:=h(10)*h(b); writeln(a,b); b:=10; a:=h(b)*h(10); writeln(a,b) end. Solución. La función h modifica una variable global. Lo cual produce un efecto lateral o secundario; es decir, el bloque h tiene una entrada (parámetro formal x) y dos salidas : el valor h de la función y la modificación de b. 100 0 0 0 Prof. Leopoldo Silva Bijit. 07-07-2003 238 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. 10000 -10 18.7.2. Determinar que escribe el programa. Explicando características de la función y el procedimiento (parámetros, forma de intercambio de datos). program demo(output); var x,y,n : integer; function gcd(m,n : integer):integer; begin if n=0 then gcd:=m else gcd:=gcd(n,m mod n) end; procedure try(a,b : integer); begin writeln(a,b,gcd(a,b)) end; begin try(18,27); try(61,53); try(98,868) end. Solución. La función gcd es RECURSIVA, con dos parámetros formales enteros y de tipo entero. El procedimiento tiene dos parámetros formales enteros; efectúa una acción que depende de los valores que se pasen (parámetros valor). Obtiene el máximo común divisor entre dos enteros , y escribe, en una línea, los enteros y el mcd. 18 27 9 61 53 1 98 868 14 18.7.3. Determinar la función que realiza Max. Explicar el ambiente (tipos y variables) en que se la puede usar y dar a un ejemplo de invocación. function Max(a:vector; n:integer)real; var x:real; i:integer; begin x:=a[1]; for i:=1 to n do if x<a[i] then x:=a[i]; Max:=x end; Solución. Prof. Leopoldo Silva Bijit. 07-07-2003 239 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. El tipo vector debe ser un arreglo de reales de 1 a N. La constante N al definir el tipo vector fija la máxima dimensión de éste (en Pascal no hay arreglos dinámicos; es una estructura estática que reserva un espacio determinado al inicio y que luego no puede cambiarse durante la computación). Esto implica que al invocar la función Max el parámetro actual asociado al formal n, no debe ser mayor que N. La función obtiene el valor máximo de las componentes de un vector cuyo tipo base es real. Por esta razón la variable local x es real, y también el tipo de la función. La presencia del parámetro formal entero n permite obtener el valor máximo de un subconjunto. Por ejemplo : el valor máximo de las primeras 10 componentes (si n es 10); o el de las 20 primeras si n es 20. Ejemplo : Si b y c son de tipo vector (N=100): a:=Max(b,5); d:=Max(b,50); Max(c,10); Max(c,<expresión entera con valor menor o igual que 100>); 18.7.4. Se tiene: Type charset = set of char; ... Procedure salte(var ch:char; conjunto: charset); begin while ch in conjunto do read(ch) end; ... a) Explicar acción realizada por el procedimiento en general. b) Explicar acción específica para el llamado: salte(x,['a'..'z']); Solución. a) El procedimiento salte lee caracteres desde el archivo estándar de entrada; y al no asignarlos a ninguna variable esto implica que dichos caracteres son descartados como información (se vuelve a asignar sobre ch). Prof. Leopoldo Silva Bijit. 07-07-2003 240 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. Se descartan todos los caracteres que pertenezcan al conjunto, que se pasa como parámetro valor (de entrada). El parámetro formal ch es variable; y permite retornar valores (salida) en la variable actual correspondiente. En ésta retorna el primer caracter que no pertenezca al conjunto. b) salte(x,['a'..'z']); Retorna en x (que debe ser definida con tipo char) el primer caracter que no sea letra minúscula. 18.7.5. Se tiene la matriz a dada por: 2 1 3 a = 3 3 1 1 2 1 a) Obtener una traza de la ejecución de: for i:=1 to 3 do for j:=1 to 3 do c[i,j]:=a[a[i,j]a[j,i]]; b) Escribir la matriz c resultante. Solución. a) i j a[i,j] a[j,i] a[b,d] c[i,j]= 1 1 2 3 2 1 2 3 3 1 2 3 2 1 3 3 3 1 1 2 1 2 3 1 1 3 2 3 1 1 Prof. Leopoldo Silva Bijit. 3 3 1 1 1 1 3 3 2 c[1,1]= 3 c[1,2]= 3 c[1,3]= 1 c[2,1]= 1 c[2,2]= 1 c[2,3]= 1 c[3,1]= 3 c[3,2]= 3 c[3,3]= 2 07-07-2003 241 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. b) 3 3 1 c = 1 1 1 3 3 2 18.7.6. Determinar qué escribe el programa. Explicar. program ocho(output); var a,b,c : integer; procedure p(x:integer; var y:integer; z:integer); begin z:=x+y+z; y:=x+z; writeln(x:4,y:4,z:4) end; begin a:=5; b:=8; c:=3; p(a,b,c); p(a+b,b,a+c); p(a*b,c,a div b) end. Solución. ___5__21__16 26 81 55 405 813 408 • El parámetro formal y es variable; esto implica que se modificará la variable actual (al salir del procedimiento) • Variables globales a,b,c • Variables locales x,y,z (el procedimiento las escribe) • Debe observarse que dentro del procedimiento, la primera acción modifica a z; luego la segunda ya toma el valor recién modificado. • Los parámetros actuales (correspondientes a los formales x,z) pueden ser expresiones. Son parámetros valor (o de entrada) Traza a b c x y z 5 8 3 5 8 3 P(a,b,c) 16 21 5 21 3 --> escribe 5 21 16 Se modifica la global b. Prof. Leopoldo Silva Bijit. 07-07-2003 242 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Programación en Pascal Capítulo 18. Procedimientos y Funciones. 18.7.7. Dado el segmento: const n=10; type matriz = array[1..n,1..n] of integer; var a,b,c : matriz; begin lea(a); lea(b); sume(a,b,c); escriba(c) end. {lee por columnas desde el estándar de entrada} {suma matriz a con b y deja resultado en c} {escribe por reglones} Diseñar los procedimientos para manipular matrices: lea, sume y escriba. Solución. Procedure lea(var m:matriz); {debe ser variable para asignar valores a [a] y [b]} var i,j : 1..n; {índices locales} begin for j:=1 ton do for i:=1 to n do read(m[i,j]) end; {lazo interno i->lee secuencialmente los elementos de una columna} Procedure escriba(m:matriz); {paso de valores; solo entrada} var i,j : 1..n; {índices locales} begin for i:=1 to n do {lazo externo : el reglón} begin for j:=1 to n do write(m[i,j]); writeln end end; Procedure sume(m1,m2 : matriz; var m:matriz); var i,j : 1..n; begin for i:=1 to n do for j:=1 to n do m[i,j]:=m1[i,j]+m2[i,j] end; Prof. Leopoldo Silva Bijit. 07-07-2003 243