Sub Program As
Sub Program As
Introducción
En pos de evitar esto, e influyendo decisivamente en el estilo y la calidad del trabajo del
programador, se utiliza la técnica divide y vencerás, que consiste en particionar y estructurar un
programa en subprogramas.
Programa principal
Subprograma
Un subprograma es una porción de código que realiza una tarea específica, que puede
ser utilizado por otras entidades (programas, subprogramas). El subprograma es, en sí mismo,
un programa principal.
Problema Principal
(Programa Principal)
Procedimientos
La codificación en PASCAL:
IdentificadorDelProcedimiento
Argumento1, Argumento2, . . .
IdentificadorDelProcedimiento
(Lista de parámetros)
Acción A
Acción B
...
Acción N
R
La codificación en PASCAL:
...;
Sentencia N
END;
C
Acción A
Acción B
...
Acción N
F
La codificación en PASCAL del programa principal incluirá la palabra reservada
PROGRAM, mencionará el identificador o nombre del mismo, detallará varias secciones a
continuación, introducidas por las palabras reservadas correspondientes, entre ellas CONST,
TYPE, VAR, PROCEDURE, e incluirá entre BEGIN y END la secuencia de acciones definida
en el algoritmo:
PROGRAM IdentificadorDelPrograma;
...;
CONST . . . ;
TYPE . . . ;
VAR . . . ;
PROCEDURE IdentificadorDelProcedimiento (Lista de parámetros);
. . .;
BEGIN
Sentencia A;
Sentencia B;
...;
Sentencia N
END.
Un ejemplo simple
Dados dos números que se ingresan por teclado, mostrar por pantalla la suma y
la resta de ambos.
C
LeerDosDatos
Num1,Num2
ImprimirSuma
Num1,Num2
ImprimirResta
Num1,Num2
F
Edición Preliminar (bajo revisión) Página 3 de 25
Universidad Tecnológica Nacional Algoritmos y Estructuras de Datos
Facultad Regional Buenos Aires Proyecto CoViE – Comunidad Virtual Educativa
Ingeniería en Sistemas de Información Subprogramas
Esquema de Datos
Entrada/Salida Identificador Tipo Descripcion Bytes
E Num1 Real Valor numérico a procesar 6
E Num2 Real Valor numérico a procesar 6
R R R
Codificado en PASCAL:
PROGRAM SumaYRestaDosNumeros;
VAR
Num1, Num2: REAL;
PROCEDURE LeerDosDatos (VAR PNum1,PNum2: REAL);
BEGIN
WRITELN(‘Ingrese dos números’);
READLN(PNum1,Pnum2)
END;
PROCEDURE ImprimirSuma (PNum1,PNum2: REAL);
BEGIN
WRITELN(‘La Suma es’);
WRITELN (PNum1 + PNum2)
END;
PROCEDURE ImprimirResta(PNum1,PNum2: REAL);
BEGIN
WRITELN(‘La Resta es’);
WRITELN(PNum1 - PNum2)
END;
BEGIN
LeerDosDatos(Num1,Num2);
ImprimirSuma(Num1,Num2);
ImprimirResta(Num1,Num2)
END.
Para calcular el sueldo de los empleados se deben tener en cuenta las siguientes
consideraciones:
a) si el empleado es full-time (F) cobra $550, si no lo es (P), percibe $220. Por cada
plan de larga distancia vendido en el mes se abonan $1,5. A este monto total lo
denominaremos sueldo básico.
b)como la empresa se encuentra pasando una excelente situación económica decide
pagarle un plus a cada empleado. Éste se encuentra conformado de la siguiente
manera:
Si el empleado tiene
c)El sueldo básico más el plus que decide abonar la compañía conforman el sueldo
bruto. Sobre éste se debe efectuar un descuento para realizar los aportes
patronales correspondientes, actualmente fijado en 11 %, quedando así conformado
el sueldo neto, que es el que efectivamente percibirá el empleado.
El programa debe:
1) mostrar el código de cada empleado junto con el sueldo neto que efectivamente
cobra;
2) infomar el código y el importe del empleado que percibe el menor de todos los
sueldos e
3) informar el código y el importe del empleado que percibe el mayor de todos los
sueldos.
C
bandera <-- FALSE
fecha
codigoEmpleado <> 0
tipoEmpleado
anioIngreso
tipoEmpleado = 'F'
antiguedad
0 1, 2, 3 4, 5, 6 ELSE
sBruto <-- sBasico + sBruto <-- sBasico + sBruto <-- sBasico + sBruto <-- sBasico +
sBasico * PLUS1 / 100 sBasico * PLUS2 / 100 sBasico * PLUS3 / 100 sBasico * PLUS4 / 100
codigoEmpleado <> 0
tipoEmpleado
anioIngreso
tipoEmpleado = 'F'
antiguedad
0 1, 2, 3 4, 5, 6 ELSE
sBruto <-- sBasico + sBruto <-- sBasico + sBruto <-- sBasico + sBruto <-- sBasico +
sBasico * PLUS1 / 100 sBasico * PLUS2 / 100 sBasico * PLUS3 / 100 sBasico * PLUS4 / 100
bandera
F
NOTA:
Las siguientes son constantes: PLUS1= 2, PLUS2 = 8, PLUS3 = 10, PLUS4 = 12,
APORTE = 11, SUELDO1 = 220, SUELDO2 = 550, COMISION = 1,5.
Esquema de Datos
E/S Identificador Descripción Tipo Bytes
bandera Indica si se ingresaro o no datos boolean 1
E fecha longint 4
E/S codigoEmpleado word 2
E tipoEmpleado char 1
E cantServiciosVendidos word 2
E anioIngreso word 2
sueldo Contiene el sueldo fijo (550 y 220) real 6
sBasico Sueldo fijo más comisiones real 6
sBruto Sueldo Basico más plus real 6
antiguedad Contiene la antigüedad byte 1
S sNeto real 6
S sueldoMax real 6
S sueldoMin real 6
S codigoMax word 4
S codigoMin word 4
Leer datos
Calcular máximos
Imprimir máximos
F
Pero no está aún resuelto totalmente nuestro problema. Tenemos que ver aún cómo se
produce el intercambio de información entre las entidades de un programa, ya que los
subprogramas pueden necesitar datos a procesar, así como pueden obtener resultados que
deban devolver al módulo o programa principal.
Argumentos y Parámetros
Los parámetros y los argumentos son las vías de intercambio de información entre
las entidades de un programa.
Las sentencias que invocan a subprogramas (llamadas) constan del nombre del
subprograma y una lista de argumentos. Los valores que posean los argumentos en el
momento de la invocación son los que se transfieren al subprograma.
Los procedimientos y las funciones deben estar declarados antes de que puedan ser
referenciados en el cuerpo de un programa o subprograma.
PROGRAM NombreDelPrograma;
USES biblioteca1, biblioteca2, etc;
TYPE
tipo1 = ...;
tipo2 = ...;
...;
VAR
Var1, Var2: tipo1;
Var3, Var4: tipo2;
...;
BEGIN
Programa Principal
END.
BEGIN
...
{Cuerpo del procedimiento}
...
END;
Observaciones:
IdentificadorDelProcedimiento
IdentificadorDelProcedimiento
Cuerpo
del
subprograma
R
La letra ‘R’ en el círculo significa retorno de control del subprograma a la entidad que lo
haya invocado, en el mismo punto donde se transfirió el control.
Cuando se declaran variables en la sección VAR del programa principal, éstas pueden
ser utilizadas en sentencias en cualquier parte del programa, incluso dentro de los
subprogramas. Por esta razón se llaman variables globales o públicas.
USES
...
CONST
...
TYPE
...
...
{procedimientos y funciones}
VAR
...
De esta manera, cualquier uso que se quiera hacer de las variables globales en los
procedimientos o en las funciones provocará un error de compilación por invocar variables que
no fueron aún declaradas.
Por ejemplo:
Que un parámetro sea de entrada significa que el valor de la variable que se pasa
como argumento no cambiará aunque dentro del subprograma se modifique. Esto se debe a
que en un sector de la memoria se reserva un espacio equivalente al del argumento y es donde
se copian los mismos valores. Entonces el subprograma opera sobre el nuevo espacio de
memoria sin modificar el del argumento. A estos parámetros se los llama parámetros por
valor.
Es útil usar este tipo de parámetros si, por ejemplo, es necesario “cuidar” el contenido
de la variable que pasa como argumento.
Es posible que sea necesario, para una entidad invocadora, conocer las modificaciones
que se produzcan dentro del subprograma. Lo que se hace en esta situación es darle al
subprograma como dato la dirección de memoria de la variable que se pasa como argumento.
Se dice que este parámetro es parámetro por referencia, lo que indica que el subprograma
operará directamente sobre la variable que se pase como argumento. Si se modifica el valor
del parámetro dentro del subprograma se está modificando el argumento. En Pascal, para
obtener un parámetro por referencia se antepone la palabra reservada VAR a la declaración de
éste:
C
'Ingrese la fecha (AAAAMMDD) :'
fecha
IngresarCodigoEmpleado
codigoEmpleado
codigoEmpleado <> 0
LeerDatos
cantServiciosVendidos, tipoEmpleado, anioIngreso
CalcularSueldoNeto
tipoEmpleado, sNeto, fecha, cantServiciosVendidos, anioIngreso
ImprimirEmpleado
codigoEmpleado, sNeto
IngresarCodigoEmpleado
codigoEmpleado
codigoEmpleado <> 0
LeerDatos
cantServiciosVendidos, tipoEmpleado, anioIngreso
CalcularSueldoNeto
tipoEmpleado, sNeto, fecha, cantServiciosVendidos
ImprimirEmpleado
codigoEmpleado, sNeto
CalcularMaximos
sueldoMax, codigoMax, sueldoMin, codigoMin, sNeto, codigoEmpleado
IngresarCodigoEmpleado
codigoEmpleado
bandera
ImprimirMaximos
codigoMax, sueldoMax, codigoMin, sueldoMin
F
Edición Preliminar (bajo revisión) Página 13 de 25
Universidad Tecnológica Nacional Algoritmos y Estructuras de Datos
Facultad Regional Buenos Aires Proyecto CoViE – Comunidad Virtual Educativa
Ingeniería en Sistemas de Información Subprogramas
Los procedimientos
IngresarCodigoEmpleado:
IngresarCodigoEmpleado
VAR codigoEmpleado: word
codigoEmpleado
R
E/S Identificador Descripción Tipo Bytes
E/S CodigoEmpl word 2
Observaciones:
-Este procedimiento imprime un literal por pantalla y obtiene el código de empleado
-El código de empleado es un parámetro por referencia pues es de salida.
LeerDatos:
LeerDatos
VAR cantServVend:word; VAR tipoEmpl:char; VAR anioIng:word
tipoEmpl
cantServVend
anioIng
Observaciones:
-Obtiene todos los datos que corresponden a cada empleado (salvo el código de
empleado).
-Todos sus parámetros son por referencia.
SumarPorc:
SumarPorc
VAR sumaP: real; x, y: real
R
E/S Identificador Descripción Tipo Bytes
E/S sumaP Contiene la suma del valor más un porcentaje del mismo Real 6
E x Contiene el valor a sumarle un porcentaje Real 6
E y Porcentaje a sumarle a X Real 6
Observaciones:
-Como lo que interesa saber es el resultado de la suma de un valor inicial más un
porcentaje de ese valor, debe haber un parámetro que devuelva el resultado (sumap).
-Este procedimiento debe declararse antes de ser usado en CalcularSueldoNeto.
CalcularSueldoNeto:
CalcularSueldoNeto
tipoEmpleado: char; VAR sNeto:real; fecha:longint; cantServiciosVendidos:word; anioIngreso:longint
tipoEmpleado = 'F'
antiguedad
0 1, 2, 3 4, 5, 6 ELSE
sumarPorc
sNeto, sBruto, APORT
Observaciones:
-Para calcular el sueldo neto el único parámetro de salida es SNeto.
-Sueldo, SBasico, SBruto y F son variables locales.
-SUELDO1, SUELDO2, PLUS1, PLUS2, PLUS3, PLUS4, COMISION, APORTE, son
constantes. Las constantes son globales.
CalcularMaximos:
CalcularMaximos
VAR sMax:real; VAR codMax:word; VAR sMin:real; VAR codMin:word; sNeto:real; codEmpl:word
Observaciones:
-Devuelve cuatro valores de salida: SMin, SMax, CodMin, CodMax, que son los sueldos
Máximo y Mínimo y a quién corresponden (CodMax y CodMin).
ImprimirEmpleado:
ImprimirEmpleado
codigoEmpleado:word, sNeto:real
Observaciones:
-Imprime el sueldo y el código del empleado que se está procesando.
-Todos sus parámetros son de entrada.
ImprimirMaximos:
ImprimirMaximos
codMax:word; sMax:real; codMin:word; sMin:real
Observaciones:
-Imprime el sueldo máximo y el sueldo mínimo y a quién corresponden.
Código Fuente
Nota:
-Se supone que los datos son ingresados correctamente.
-En rojo aparecen las constantes, en negro las palabras reservadas de Pascal y en
azul los identificadores de los procedimientos definidos por el usuario.
-El ejercicio pudo haber sido resuelto utilizando menos variables, se hizo así con fines
pedagógicos y de legibilidad.
-El ejercicio pudo haber sido resuelto utilizando menos repetición de código. Se hizo de
esta manera para mantener la lógica del programa en primer lugar y para resaltar el uso de
procedimientos y funciones en segundo lugar.
PROGRAM EjDeProcedimientos;
USES crt;
CONST
PLUS1 = 2;{Plus para empleados con 0 años de
antigüedad}
PLUS2 = 8;{Plus para empleados de 1 a 3 años de
antigüedad}
PLUS3 =10;{Plus para empleados de 4 a 6 años de
antigüedad}
PLUS4 =12;{Plus para empleados con más de 6 años de
antigüedad}
SUELDO1 = 550;{Sueldo fijo de empleados de tiempo
completo}
SUELDO2 = 220;{Sueldo fijo de empleados de medio
tiempo}
COMISION = 1.5;{Comisión por servicio vendido}
APORTE = -11;{Descuento por aportes}
VAR
sueldoNeto, sueldoMin, sueldoMax: real;
i, codigoEmpleado, CantServiciosVendidos,
anioIngreso, codigoMin, codigoMax: word;
fecha: longint;
tipoEmpleado: char;
antiguedad: byte;
bandera: boolean;
BEGIN
SumaP := x + (x * y) / 100;
END; {Note la devolución de un único valor y además
representativo del procedimiento}
VAR
sBruto, sBasico, sueldo: real;
antiguedad: byte;
BEGIN
TipoEmpl := UpCase(TipoEmpl);{Es una función
predefinida. Si el argumento
que se
le pasa a Upcase es una letra
minúscula la pasa a
mayúscula}
IF TipoEmpl = 'F' THEN
sueldo := SUELDO1
ELSE
sueldo := SUELDO2;
CASE antiguedad OF
0: SumarPorc (sBruto, sBasico, PLUS1);
1,2,3: SumarPorc (sBruto, sBasico, PLUS2);
4,5,6: SumarPorc (sBruto, sBasico, PLUS3)
ELSE
SumarPorc (sBruto, sBasico, PLUS4)
END;
SumarPorc (sNeto, SBruto, APORTE);
END; {Note la devolución de un único valor y además
representativo del procedimiento}
PROCEDURE CalcularMaximos (VAR sMax: real; VAR CodMax: word; VAR SMin:
real; VAR CodMin: word; sNeto: real;
codigoEmpl: word);
BEGIN
IF (sNeto > sMax) THEN
BEGIN
sMax := sNeto;
codMax := codigoEmpl;
END
ELSE
IF (sNeto < sMin) THEN
BEIGN
sMin := sNeto;
codMin := codigoEmpl;
END;
END;
BEGIN
clrscr; {Limpia la pantalla y pone el cursor en (X,Y) = (1,1)}
write ('Ingrese la fecha de hoy (AAAAMMDD): ');
readln (fecha);
IngresarCodigoEmpleado (codigoEmpleado);
bandera := false;
IF (codigoEmpleado <> 0) THEN
BEGIN
bandera := true;
LeerDatos (codigoEmpleado, cantServiciosVendidos,
tipoEmpleado, anioIngreso);
CalcularSueldoNeto (tipoEmpleado, sNeto, fecha,
cantServiciosVendidos, anioIngreso);
ImprimirEmpleado (codigoEmpleado, sNeto);
sueldoMax := sNeto;
codigoMax := codigoEmpleado;
sueldoMin := sNeto;
codigoMin := codigoEmpleado; {Estas asignaciones se
podrían hacer dentro de
un procedimiento}
IngresarCodigoEmpleado (codigoEmpleado);
END;
WHILE (CodigoEmpl <> 0) DO
BEGIN
LeerDatos (codigoEmpleado, cantServiciosVendidos,
tipoEmpleado, anioIngreso);
CalcularSueldoNeto (tipoEmpleado, sNeto, fecha,
cantServiciosVendidos, anioIngreso);
ImprimirEmpleado (codigoEmpleado, sNeto);
CalcularMaximos (sNeto, sueldoMax, codigoMax,
CodigoEmpleado, sueldoMin,
codigoMin);
IngresarCodigoEmpleado (codigoEmpleado);
END;
END.
Como podemos observar, el procedimiento para calcular sueldo neto devuelve un único
valor (sNeto) al programa principal. Además, este valor es representativo del subprograma.
Notamos que sucede lo mismo con el procedimiento SumarPorc.
Funciones
Es posible que el usuario pueda declarar sus propias funciones de igual modo que
declara sus procedimientos.
VAR
BEGIN
...
{Desarrollo de la función}
...
END;
Observaciones:
-Todo cuerpo de una función comienza con BEGIN y termina con END;
-La estructura de una función es igual a la de un programa Pascal
-Tipo corresponde al tipo de dato que devuelve la función en su nombre.
SumaPorc
(x, y: real) :real
R
Observaciones:
- El resultado se asigna directamente al identificador de la función.
Nota: También se utiliza un encabezamiento del siguiente tipo, para indicar lo mismo:
SumaPorc
x, y: real
:real
SNeto
(tipoEmpleado: char; VAR sNeto:real; fecha:longint; cantServiciosVendidos:word; anioIngreso:longint) :real
tipoEmpleado = 'F'
antiguedad
0 1, 2, 3 4, 5, 6 ELSE
Observaciones:
-El resultado del subprograma es devuelto en el identificador.
Su codificación sería:
BEGIN
SumarPorc := x + (x * y) / 100;
END;
VAR
sBruto, sBasico, sueldo: real;
antiguedad: byte;
BEGIN
TipoEmpl := UpCase(TipoEmpl);
IF TipoEmpl = 'F' THEN
sueldo := SUELDO1
ELSE
sueldo := SUELDO2;
CASE antiguedad OF
0: sbruto:= SumarPorc(sBasico, PLUS1);
1,2,3: sBruto:= SumarPorc(sBasico, PLUS2);
4,5,6: sBruto:= SumarPorc(sBasico, PLUS3)
ELSE
sBruto:= SumarPorc(sBasico, PLUS4);
END;
SNeto := SumarPorc (sBruto, APORTE);
END;
BEGIN
clrscr; {Limpia la pantalla y pone el cursor en (X,Y) = (1,1)}
write ('Ingrese la fecha de hoy (AAAAMMDD): ');
readln (fecha);
IngresarCodigoEmpleado (codigoEmpleado);
Bandera := false;
IF (codigoEmpleado <> 0) THEN
BEGIN
bandera := true;
LeerDatos (codigoEmpleado, cServiciosVendidos,
tipoEmpleado, anioIngreso);
sN := Sneto (tipoEmpleado, fecha,
cServiciosVendidos,
anioIngreso);{Habría
que declarar una nueva
variable sn, no puede haber
una variable con el mismo
nombre de una función o de un
procedimiento}
ImprimirEmpleado (codigoEmpleado, sNeto);
SMax := sN;
CodMax := codigoEmpl;
SMin := sN;
CodMin := CodigoEmpl;
IngresarCodigoEmpleado (codigoEmpleado);
END;
WHILE (codigoEmpleado <> 0) DO
BEGIN
LeerDatos (codigoEmpleado, cServiciosVendidos,
tipoEmpleado, anioIngreso);
sN := Sneto (tipoEmpleado, fecha,
cServiciosVendidos, anioIngreso);
ImprimirEmpleado (codigoEmpleado, sNeto);
CalcularMaximos (sN, sueldoMax, CodigoMax,
codigoEmpleado, sueldoMin,
codigoMin);
IngresaCodigoEmpleado (codigoEmpleado);
END;
IF Bandera THEN
ImprimirMaximos (codigoMax, sueldoMax, codigoMin,
sueldoMin);
readln;
END.
En lugar de guardar en Sn el valor del sueldo neto, se podría usar a la función como
argumento de los procedimientos que requieren el sueldo neto. Esto implicaría invocar la
función muchas veces, lo que significaría hacer muchas veces el mismo cálculo.
Ejemplo 1:
PROGRAM TipoEnumeradoEnCicloExacto;
. . . ;
PROCEDURE ProcesarDatos(PMes:Meses);
. . . ;
TYPE
Meses = (Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio,
Agosto, Setiembre,
Octubre, Noviembre, Diciembre);
VAR
Mes: Meses;
BEGIN
. . . ;
FOR Mes := Marzo TO Diciembre DO
ProcesarDatos(Mes);
. . . ;
Ejemplo 2:
PROGRAM AnidamientoDeCiclos;
. . . ;
TYPE
Vocales = (a, e, i, o, u);
Texto = STRING[30];
. . . ;
PROCEDURE ImprimirVocal(Pvocal:Vocales);
BEGIN
CASE PVocal OF
a: WRITELN(‘a’);
e: WRITELN(‘e’);
i: WRITELN(‘i’);
o: WRITELN(‘o’);
u: WRITELN(‘u’)
END
END;
. . . ;
FUNCTION CoincidenPrimeraLetra(PPalabra: Texto; Pvocal: Vocal):
BOOLEAN;
VAR
Aux: CHAR;
BEGIN
CASE Pvocal OF
a: Aux := 'a';
e: Aux := 'e';
i: Aux := 'i';
o: Aux := 'o';
u: Aux := 'u'
END;
CoincidenPrimeraLetra := Aux = PPalabra[1]
END;
VAR
Vocal: Vocales;
Palabra: Texto;
BEGIN
. . . ;
FOR Vocal := a TO u DO
BEGIN
WRITELN(‘Ingrese palabras que comiencen con ‘,
ImprimirVocal(Vocal));
READLN(Palabra);
WHILE CoincidenPrimeraLetra(Palabra, Vocal) DO
BEGIN
WRITELN(‘Ingrese palabras que comiencen con ‘,
ImprimirVocal(Vocal));
READLN(Palabra)
END;
WRITE(‘ERROR, entonces ahora . . .);
END;
. . . ;
Para evitar este error se debe definir un tipo STRING[5] y utilizar ese tipo para declarar
el parámetro cadena.
TYPE
tCad5 = STRING[5];
...
PROCEDURE EjemploDeLoQueSiSeHace (cadena: tCad5);
...
VAR
cad: tCad5;
BEGIN
...
EjemploDeLoQueSiSeHace (cad);
...
END.
Observaciones:
-cad debe ser estrictamente de tipo tCad5, pues tCad5 es un string[5] pero no es el
mismo tipo. Si no es el mismo tipo, entonces, como ya se ha mencionado, es un error pasar
como argumento de un subprograma a una variable de distinto tipo que el parámetro
declarado.