Mostrando entradas con la etiqueta Lógica de Programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta Lógica de Programación. Mostrar todas las entradas

lunes, 28 de septiembre de 2015

Lógica de programación (31) - Operaciones con vectores



Como ya mencionamos en la lección anterior, un vector es una secuencia ordenada de elementos. A fin de cuentas, los vectores son variables con las que podemos realizar diferentes operaciones como son:
  • Declaración
  • Asignación/Escritura de valores
  • Recorrido
  • Actualización
  • Ordenación
  • Búsqueda


En esta lección daremos un recorrido por las primeras cuatro de estas operaciones y dejaremos la Ordenación y la Búsqueda para lecciones posteriores.

Declaración

Por lo general, un array se declara en la sección de variables de un programa. La forma de declarar un array de elementos de un determinado tipo es muy variada en los distintos lenguajes de programación. Sin embargo, para los efectos de este curso, esta es la sintaxis que estaremos siguiendo:

array[num] de tipo : nombre_array

donde
  • num es la cantidad de elementos que tendrá el array
  • tipo es el tipo de datos de los elementos del array (entero, booleano, cadena, etc)
  • nombre_array es el nombre del arreglo


Asi pues, la expresión:

array[10] de entero : números

representa la declaración de un arreglo de 10 elementos de tipo entero, cuyos índices irán del 0 al 9.

Una vez declarado el arreglo, nos es posible acceder a cada uno de sus elementos usando el nombre del arreglo seguido del índice del elemento al cual deseamos acceder, como lo estudiamos en la lección anterior.

Asignación/Escritura de valores

Una forma de asignar valores a un elemento del vector es mediante la instrucción de asignación simple; por ejemplo, la expresión números[3] <- 5 asigna el valor 5 al elemento de índice 3 dentro del vector números.

Otra forma de hacerlo es usando la instrucción leer() como con cualquier otra variable, por ejemplo:

leer(números[0])
leer(números[1])

En cada una de las dos formas descritas podemos asignar valores a sólo uno de los elementos del vector a la vez. Si quisiéramos asignar valores a todos los elementos del vector, tendríamos que recurrir a estructuras repetitivas como en el siguiente ejemplo, en el cual usamos un ciclo desde para asignar a cada uno de los elementos del vector números un valor dado por el usuario:

algoritmo Ejemplo_Vector
var
    array[10] de entero: números
    entero: i
inicio
    desde i <- 0 hasta 9 hacer
        escribir("Proporciona un valor para el elemento con índice " + i + " del arreglo: ")
        leer(números[i])
    fin_desde
fin


Recorrido (acceso secuencial) del vector

Lo que hicimos en el ejemplo anterior fue un recorrido del vector para escribir datos en él. Se le llama recorrido a la operación de efectuar una operación general sobre todos los elementos del vector, ya sea escribir datos o leer su contenido. Estas operaciones se realizan utilizando estructuras repetitivas, cuyas variables de control se usan como índices del vector. El incremento del valor de la variable de control permite que podamos acceder secuencialmente a cada uno de los elementos del vector.

Complementaremos el ejemplo anterior para incluir ahora un recorrido de lectura que se efectuará inmediatamente después de que el usuario haya terminado de introducir valores en el vector.

algoritmo Ejemplo_Vector
var
    array[10] de entero: números
    entero: i
inicio

    //Asignar valores a cada elemento del vector
    desde i <- 0 hasta 9 hacer
        escribir("Proporciona un valor para el elemento con índice " + i + " del arreglo: ")
        leer(números[i])
    fin_desde

    //Recorrer el vector para mostrar los números introducidos
   desde i<-0 hasta 9 hacer
        escribir(números[i])
    fin_desde

fin


En este otro ejemplo solicitamos al usuario un conjunto de 15 calificaciones que almacenamos en un vector. Luego recorremos ese vector para calcular la suma de todas las calificaciones y finalmente calculamos y mostramos el promedio de las mismas:

algoritmo Promedio
var
    array[15] de real: calif
    entero: i
    real: suma, promedio
inicio
    //Asignar calificaciones al vector
    desde i <- 0 hasta 14 hacer
        escribir("Escribe una calificación")
        leer(calif[i])
    fin_desde

    //Recorrer el vector para calcular la suma de las calificaciones
    suma <- 0
    desde i <- 0 hasta 14 hacer
        suma <- suma + calif[i]
    fin_desde

    //Calcular y mostrar el promedio
    promedio <- suma / 15
    escribir("El promedio de las calificaciones proporcionadas es " + promedio)
fin


Actualización

La actualización de un vector puede constar a su vez de tres operaciones elementales que son: añadir, insertar o borrar elementos.

Añadir datos consiste en agregar un nuevo elemento al final del vector. La única condición necesaria para llevar a cabo esta operación es que exista espacio suficiente en el vector para recibir al nuevo elemento, es decir, que el vector no esté lleno. Si esta condición se cumple, basta con referirnos a la posición en la que deseamos insertar el nuevo elemento para agregarlo. Ahora bien, si esta posición no se conoce y se desea agregar un elemento nuevo al final del vector verificando de antemano si existe lugar para ello, puede realizarse con un algoritmo como el del siguiente ejemplo:

algoritmo Añadir
var
   array[5] de cadena: colores
   entero: i
inicio
   //Asignar algunos valores al arreglo 'colores'
   colores[0] <- "verde"
   colores[1] <- "rojo"
   colores[2] <- "azul"
   
   //Insertar un nuevo color al final
   desde i<-0 hasta 4 hacer
      si (colores[i] <- nulo) entonces
         colores[i] <- "amarillo"
         interrumpir
      fin_si
   fin_desde
fin

En el ejemplo anterior recorremos el arreglo colores hasta encontrar una casilla cuyo valor sea nulo y en ella insertamos un nuevo color, terminando el recorrido en el proceso. Una vez que termina el algoritmo, nuestro arreglo contendrá los siguientes elementos:

colores[0] = verde
colores[1] = rojo
colores[2] = azul
colores[3] = amarillo
colores[4] = *nulo*

Si bien esta no es la única forma de insertar un nuevo elemento en un arreglo, es una solución sencilla y rápida para lograrlo.

A diferencia de la operación de Añadir, cuando ejecutamos una operación  Insertar lo que buscamos es colocar un nuevo elemento dentro de un arreglo en una posición específica y no necesariamente al final del mismo. En este caso aplica la misma condición que con la operación Añadir: debe existir espacio suficiente en el arreglo para insertar un nuevo elemento. Con unos pequeños ajustes, podemos emplear el algoritmo del ejemplo anterior para demostrar la operación de inserción:

algoritmo Añadir
var
   array[5] de cadena: colores
   entero: i
inicio
   //Asignar algunos valores al arreglo 'colores'
   colores[0] <- "verde"
   colores[1] <- "rojo"
   colores[3] <- "azul"
   
   //Insertar un nuevo color al final
   desde i<-0 hasta 4 hacer
      si (colores[i] <- nulo) entonces
         colores[i] <- "amarillo"
         interrumpir
      fin_si
   fin_desde
fin

Este algoritmo inserta algunos valores en las posiciones 0, 1 y 3 del arreglo y luego lo recorre para insertar un nuevo elemento en la primera posición vacía que se encuentre (en este caso, será la posición 2), quedando finalmente nuestro arreglo con los siguientes elementos:

colores[0] = verde
colores[1] = rojo
colores[2] = amarillo
colores[3] = azul
colores[4] = *nulo*

Finalmente, la operación de Borrar consiste en eliminar de un arreglo un elemento ya sea indicando su posición dentro del mismo o realizando primero una búsqueda y eliminando el elemento una vez que se ha localizado dentro del arreglo. Existen varias formas de realizar el borrado de un elemento de un arreglo, pero dejaremos los ejemplos para cuando aprendamos a realizar búsquedas dentro de un vector.


ACTIVIDADES DE APRENDIZAJE


  1. Escribe un algoritmo que lea los valores de 100 temperaturas y a partir de ellas: (a) Calcule y muestre el promedio de las temperaturas y (b) muestre todas las temperaturas que se encuentran por encima del promedio.
  2. Escribe un algoritmo que te permita calcular la desviación estándar del conjunto de temperaturas del problema anterior, sabiendo que la desviación se calcula con la fórmula siguiente:


     


Enlace: Índice del curso




miércoles, 26 de agosto de 2015

Lógica de Programación (30) - Arrays unidimensionales: los vectores

Todos hemos sido parte de un array en algún momento...

Un array o arreglo se define formalmente como un conjunto finito y ordenado de elementos homogéneos. Cuando decimos "ordenado" nos referimos al hecho de que cada uno de los elementos puede ser identificado por un índice numérico; por otro lado, decimos que el conjunto es "homogéneo" porque todos los elementos son del mismo tipo de dato, es decir, todos son cadenas o enteros o booleanos, etcétera. El tipo más simple de array es el array unidimensional, también conocido como vector

En un vector, cada uno de sus elementos puede ser identificado mediante el uso de un índice numérico que nos indica su posición dentro del mismo, mientras que sólo el vector completo tiene un nombre propio. En la siguiente figura se representa un vector llamado "Calificaciones" que contiene 5 elementos, numerados del 0 al 4 *.


Para referirnos a cada uno de los elementos del vector, usamos la siguiente notación:

VECTOR[índice]

Por ejemplo:

Calificaciones[1] tiene un valor de 10
Calificaciones[4] tiene un valor de 7

Además, cada elemento del vector puede ser tratado como si fuera una variable independiente. Por ejemplo, podemos tener una variable Suma cuyo valor sea la suma de dos o más elementos del vector:

Suma <- Calificaciones[2] + Calificaciones[4]

En este ejemplo, la variable Suma tendría un valor de 15.

Si deseamos asignar un valor a uno de los elementos del vector, basta con referirnos al elemento por su índice y asignarle el valor deseado. Así, la expresión Calificaciones[3] <- 8 asigna el valor de 8  a la posición 3 del vector, sustituyendo cualquier valor que haya estado almacenado previamente.

Por otro lado, los índices de un vector pueden ser enteros, variables o expresiones enteras. Por ejemplo, si tenemos una variable i <- 1

Calificaciones[i] representa el elemento Calificaciones[1] cuyo valor es 10
Calificaciones[i+3] representa el elemento Calificaciones[4] cuyo valor es 7

El hecho de que podamos acceder individualmente a cada uno de los elementos del array representa una de sus ventajas más importantes: el almacenamiento de información.

NOTAS

* La mayoría de los lenguajes de programación comienzan en 0 la numeración de las posiciones de los elementos de un array. Así, un vector de 10 elementos los verá numerados del 0 al 9. A esta práctica se le conoce como indexación base cero.

ACTIVIDADES DE APRENDIZAJE

Considera el siguiente vector:



Escribe el resultado de cada una de las siguientes operaciones realizadas con los elementos de dicho vector:
  • escribir(Números[1])
  • Números[4] <- 32.5
  • Suma <- Números[1] + Números[5] + Números[6]
  • Suma <- Suma + Números[5]
  • Números[i+2] = Números[i] + Números[i+1] (suponiendo que i<-0)

jueves, 9 de julio de 2015

Lógica de Programación (29) - Introducción a las estructuras de datos

¿Estructuras complejas formadas por muchas unidades pequeñas? Existen, y un edificio de departamentos es un buen ejemplo de ello.


En las lecciones anteriores se ha introducido el concepto de datos de tipo simple que representan valores de tipo simple, como un número entero, real o un carácter. En muchas situaciones se necesita, sin embargo, procesar una colección de valores que están relacionados entre sí por algún método, por ejemplo, una lista de calificaciones, una serie de temperaturas medidas a lo largo de un mes, etc. El procesamiento de tales conjuntos de datos, utilizando datos simples, puede ser extremadamente difícil y por ello la mayoría de los lenguajes de programación incluyen características de estructuras de datos. Las estructuras de datos básicas que soportan la mayoría de los lenguajes de programación son los «arrays» —concepto matemático de «vector» y «matriz»—. Un array o arreglo en Latinoamérica es una secuencia de posiciones de la memoria central a las que se puede acceder directamente, que contiene datos del mismo tipo y pueden ser seleccionados individualmente mediante el uso de subíndices. A partir de esta lección estudiaremos el concepto de arrays unidimensionales y multidimensionales, así como el procesamiento de los mismos. 

La importancia de las computadoras radica fundamentalmente en su capacidad para procesar información. Esta característica les permite realizar actividades que antes sólo las realizaban los humanos. Con el propósito de que la información sea procesada, se requiere que ésta se almacene en la memoria de la computadora. 

De acuerdo con la forma en que los datos se organizan, se clasifican en: 

  • Tipos de datos simples. 
  • Tipos de datos estructurados. 


La principal característica de los tipos de datos simples consiste en que ocupan sólo una casilla de memoria; por tanto, una variable simple hace referencia a un único valor a la vez. En este grupo de datos se encuentran: números enteros y reales, caracteres, booleanos, enumerados y subrangos. Cabe señalar que los dos últimos no existen en algunos lenguajes de programación. 

Por otra parte, los tipos de datos estructurados se caracterizan por el hecho de que con un nombre -identificador de variable estructurada- se hace referencia a un grupo de casillas de memoria. Es decir, un tipo de dato estructurado tiene varios componentes. Cada uno de éstos puede ser un tipo de dato simple o estructurado. Sin embargo, los componentes básicos, los del nivel más bajo, de cualquier tipo de datos estructurado son siempre tipos de datos simples. 

O, para representarlo de un modo más gráfico:


En la siguiente lección vamos a conocer la estructura de datos más simple: el arreglo unidimensional. ¡Hasta la próxima!



Enlace: Índice del curso




domingo, 5 de julio de 2015

Ejercicios sobre funciones



Resuelve los siguientes problemas para practicar los conceptos relativos a las funciones y procedimientos que aprendiste en el curso de Lógica de Programación. 

  1. Diseña una función que calcule el promedio de tres números dados por el usuario.
  2. Diseña una función que reconozca y regrese el mayor de dos números dados por el usuario.
  3. Escribe un procedimiento que acepte un número de día, un número de mes y un número de año y los muestre en el formato dd/mm/aa. Por ejemplo, los valores 19, 9, 1978 se mostrará 19/9/87. Asegurate de validar que los números de día y mes sean válidos, es decir, que el mes esté entre 1 y 31 y el mes esté entre 1 y 12.
  4. Escribe una función llamada Salario que calcule los salarios de un trabajador para un número dado de horas trabajadas y un salario por hora. Las horas que superen las 40 horas semanales se pagaran como extras con un salario hora de 1.5 veces del salario regular.
  5. Escribe una función booleana llamada Digito que determine si un caracter dado es uno de los dígitos del 0 al 9.
  6. Diseña una función que retorne el valor absoluto de un número dado.




Lógica de Programación (28) - Recursividad

Infinite Will Wheatons!!!

Reza una regla muy conocida en el lenguaje que un término definido no puede ser incluido dentro de su propia definición. Sin embargo, en programación esta regla no existe y a dicha aparente "violación" se le conoce como recursividad y en esta lección vamos a aprender cómo funciona este útil y alucinante concepto de la programación.

Como ya hemos estudiado en todo este bloque, un subprograma puede llamar a cualquier otro subprograma y éste a otro y así sucesivamente; dicho de otro modo, los subprogramas se pueden anidar. Se puede tener:

A llamar_a B, B llamar_a C, C llamar_a D

Cuando se produce el retorno de los subprogramas, a la terminación de cada uno de ellos el proceso resultante será: 

D retornar_a C, C retornar_a B, B retornar_a A

Pero, ¿qué sucedería si los programas de una secuencia de este tipo son los mismos?

A llamar_a A

o bien

A llamar_a B, B llamar_a A

Esto, en primera instancia, parecería incorrecto. Sin embargo la gran mayoría de los lenguajes de programación incluyen mecanismos por los que un subprograma puede llamarse a sí mismo.

Una función o procedimiento que puede llamarse a sí mismo se llama recursivo. La recursividad es una herramienta muy potente, sobre todo en aquellas aplicaciones destinadas a hacer cálculos matemáticos o estadísticos. La recursividad también puede ser utilizada como una alternativa a las estructuras repetitivas. Si un problema puede definirse de modo natural en términos recursivos, entonces podemos programar una aplicación usando la recursividad para dicho problema.

Escribir un procedimiento o función recursivo es muy similar a escribir uno que no lo sea; sin embargo para evitar que la recursión continue indefinidamente es necesario que se incluya una condición de terminación.

Uno de los problemas más útiles para explicar la recursividad es el cálculo del factorial de un número (n!). Recordemos que el factorial se define de la siguiente manera:

n! = n * (n-1) * (n-2) * ... *3 * 2 * 1

Por ejemplo, el factorial de 5 se calcularía de este modo:

5! = 5 * 4 * 3 * 2 * 1

Sin embargo, observa que 4 * 3 * 2 * 1 es 4!, por lo que podríamos reescribir la expresión así:

5! = 5 * 4!

a la vez, observamos que 3 * 2 * 1 es igual a 3!, por lo que podría decirse que

4! = 4 * 3!

Si continuamos con dicha secuencia, podemos concluir que la función factorial es una función recursiva, pues para calcular el factorial de un número n requerimos calcular el factorial de n-1, es decir:

factorial <- n * factorial(n-1)

La definición de una función algorítmica sería entonces como sigue:

entero función factorial (entero n)
inicio
   si n = 0 entonces
      devolver (1)
   si_no
      devolver (n * factorial(n-1))
   fin_si
fin_función

Para demostrar cómo esta función recursiva calcula el factorial de 3, usemos el siguiente gráfico:



Observa que cada llamada a la función recursiva lleva como parámetro el número al que corresponda el turno. En la última llamada dicho número es 0, por lo que no se vuelve a llamara a la función recursiva sino que se retorna el valor de 1, con lo cual se van retornando sucesivamente los valores de las llamadas precedentes.

La recursividad es sin duda una de las herramientas más útiles con las que cuenta la programación y, aunque puede parecer confusa al principio, una vez que se comprende nos ayuda a crear aplicaciones más potentes, rápidas y con poco gasto de recursos de cómputo.

Con esta lección terminamos la introducción a los conceptos relativos a los subalgoritmos. En la siguiente lección voy a compartir una lista de ejercicios que pueden resolver para practicar todo lo relativo a las funciones y, después de eso, comenzaremos con la última sección de este curso de Lógica de Programación. ¡Hasta la próxima!








martes, 30 de junio de 2015

Lógica de Programación (27) - Comunicación con subprogramas: paso de parámetros



Cuando un programa llama a un subprograma, la información se comunica a través de una lista de parámetros o argumentos. La forma más común para realizar este paso de información es utilizando una correspondencia posicional, es decir, que los valores de los parámetros se asignan en el mismo orden en que se pasan.

Por ejemplo, supongamos que tenemos un procedimiento definido del siguiente modo:

procedimiento division (entero dividendo, entero divisor)
...
...
fin_procedimiento

y una llamada a dicho procedimiento desde nuestro programa principal, expresada asi:

division(20, 5)

Los valores 20 y 5, pasados como parámetros al procedimiento división, se asignarán en el orden que se pasan. Es decir, la variable dividendo tomará el valor de 20 y divisor el valor de 5.

Existen diferentes métodos para la transmisión de parámetros a los subprogramas, entre los cuales se cuentan:
  • Paso por valor
  • Paso por referencia
  • Paso por nombre
  • Paso por resultado


Los dos primeros son los más frecuentes en la gran mayoría de los lenguajes de programación, por lo que nos vamos a enfocar a explicarlos a detalle.

Paso por valor

Un parámetro es pasado por valor cuando lo que se le pasa al subprograma es una copia del valor de una variable, el cual recibe en una variable local al subprograma, de modo que cualquier operación realizada sobre dicho valor no afectará ni cambiará al valor original. Tomemos, por ejemplo, el siguiente algoritmo:

procedimiento cambiar(entero a, entero b)
var
   entero: aux
inicio
   aux <- a
   a <- b
   b <- aux
fin_procedimiento

algoritmo Paso_Por_Valor
var
   entero: a, b   
inicio
   a <- 5
   b <- 50
   escribir(a, b)
   cambiar(a, b)
   escribir(a,b)
fin

El algoritmo principal contiene dos variables, cuyos valores son 5 y 50, respectivamente. Luego invoca al procedimiento cambiar, el cual toma una copia de esos valores y los usa en sus operaciones internas. Dentro de dicho procedimiento, la variable a tomará el valor de 5 y la variable b tomará el valor de 50. Las operaciones del procedimiento tienen el objetivo de intercambiar dichos valores por lo que, una vez realizadas, las variables a y b tendrán los valores intercambiados. Pero dado que el procedimiento trabaja con una copia de los datos, las variables originales conservan sus mismos valores. Es decir, cuando volvamos a escribir en pantalla los valores de a y b, veremos los mismos valores que originalmente les asignamos.

Paso por referencia

Ahora bien, cuando pasamos parámetros por referencia, en realidad estamos pasando a un subprograma un indicador o apuntador que señala a la posición de memoria donde se almacena el valor de una variable, de modo que todas las operaciones que se hacen sobre dichos valores dentro del subprograma afectan y cambian a los mismos. En el paso por referencia no se pasa una copia de los datos, sino las referencias a los datos originales. Para indicar que un parámetro está siendo pasado por referencia, utilizamos la palabra reservada var delante de cada uno de los argumentos que especifiquemos en la definición del subprograma.

Observa el ejemplo anterior pero ahora modificado para recibir los parámetros por referencia:

procedimiento cambiar(var entero a, var entero b)
var
   entero: aux
inicio
   aux <- a
   a <- b
   b <- aux
fin_procedimiento

algoritmo Paso_Por_Valor
var
   entero: a, b   
inicio
   a <- 5
   b <- 50
   escribir(a, b)
   cambiar(a, b)
   escribir(a,b)
fin

Observa que hemos añadido la palabra reservada var delante de cada uno de los parámetros incluidos en la definición del procedimiento. En este caso, todas las operaciones que hacemos dentro del procedimiento cambiar están afectando directamente a los valores originales de a y b, por lo que al ejecutar la segunda instrucción escribir(a,b) del programa principal, veremos que a y b han intercambiado sus valores como resultado de la ejecución del subprograma.

Es importante resaltar que en ambos ejemplos usamos los mismos nombres para las variables tanto en el programa principal como en el procedimiento. Pero eso no siempre tiene que ser así. Si usamos nombres distintos para las variables, pero especificamos que estamos pasando los valores por referencia, las variables en el subprocedimiento apuntarán a las variables originales independientemente de su nombre.

El paso de parámetros más usual y por defecto es el paso por valor. Sin embargo, existen situaciones en las que el paso por referencia es más útil, como en aquellos casos en los que los parámetros contengan una cantidad demasiado grande de información (como un arreglo, de los cuales aprenderemos más adelante).

En los algoritmos que estamos aprendiendo a crear no veremos gran diferencia entre ambos métodos de paso de parámetros, pero es importante conocer que existen para cuando apliquemos nuestros conocimientos a algún lenguaje de programación en particular.

En la siguiente lección, la última de la tercera sección de este curso, aprenderemos uno de los conceptos más extraños y útiles de la programación de computadoras: la recursividad. ¡Hasta la próxima!





domingo, 21 de junio de 2015

Lógica de Programación (26) - Ámbito de las variables

En esta lección vamos a aprender una de las más útiles características de la programación modular: el uso de variables locales y globales en nuestros programas.

Se dice que una variable es local cuando está declarada y definida dentro de algún subprograma (ya sea una función o un procedimiento). El significado y la utilidad de una variable local están limitados únicamente al subprograma donde fue definida, es decir, el valor que tenga almacenado no será accesible para ningún otro subprograma. Ahora bien, pueden definirse variables locales con el mismo nombre en distintos subprogramas, pero eso no quiere decir que se refieran a la misma posición de memoria o al mismo valor.

Por otro lado, una variable global es la que se declara en el programa o algoritmo principal, del cual dependen todos los demás subprogramas. A diferencia de las variables locales, las globales tienen la ventaja de compartir información entre subprogramas. Una variable global es útil y accesible tanto en el programa principal como en todos los subprogramas que dependan de él. Cualquier subprograma puede acceder y modificar el valor de una variable global.

El ámbito o alcance (scope, en inglés) de una variable es la parte del programa o algoritmo en el que una variable definida, conocida y acesible. En la siguiente figura podemos ver una descripción conceptual del ámbito de diferentes variables:

Ámbito de identificadores
En esta figura, podemos observar que:
  • Las variables X1 y X2 son globales por estar definidas en el algoritmo principal y serán accesibles desde cualquier parte del programa, incluidos todos los subprogramas que dependan de él. 
  • Las variables Y1 y Y2 son locales al procedimiento A y serán accesibles únicamente dentro de ese procedimiento y también por los subrogramas que dependan de él (como el procedimiento B). 
  • Las variables Z1 y Z2 son locales al procedimiento B y son accesibles única y exclusivamente por él.
  • Las variables W1 y W2 son locales al procedimiento C y son accesibles única y exclusivamente por él.
Para explicarlo aún mejor, mira este otro esquema donde podemos observar la accesibilidad que tienen las variables definidas en distintos niveles de un programa con subprogramas anidados:


En el siguiente ejemplo definimos la función signo que realiza la siguiente tarea: si el número dado es 0, entonces devuelve un 0, si es positivo, devuelve un 1 y si es negativo devuelve un -1.


algoritmo Signos
var
   entero: a, b, c
   real: x, y, z
inicio
   x <- 5.4
   a <- signo(x)
   y <- 0
   b <- signo(y)
   z <- 7.89
   c <- signo (z - 9)
   escribir ("Las respuestas son " + a +" " + b + " " + c)
fin

entero función signo (real x)
var
   entero: s
inicio
   si x = 0 entonces s <- 0
   si x > 0 entonces s <- 1
   si x < 0 entonces s <- -1
   devolver (s)
fin_función


En este ejemplo, la variable s, declarada dentro de la función signo, es local y accesible únicamente dentro de ese procedimiento.

Es importante comprender el concepto de ámbito de variables dado que muchos de los errores de programación se dan por no considerar correctamente la accesibilidad que tendrá una variable en un determinado programa. En la próxima lección aprenderemos cómo podemos aprovechar las variables locales y globales para pasar parámetros a nuestros procedimientos y funciones. ¡Hasta la próxima!


Enlace: Índice del curso



viernes, 12 de junio de 2015

Lógica de Programación (25) - Procedimientos


En la lección anterior aprendimos que las funciones reciben una serie de parámetros que luego son procesados para devolver un resultado. Dado que las funciones regresan un valor, es necesario que cada función tenga un tipo de retorno asignado, el cual se especifica cuando se declara la función.

Un procedimiento es similar a una función, pero con notables diferencias:

  • Una función devuelve sólo un valor, mientras que un procedimiento puede regresar cero, uno o muchos valores a la vez.
  • Los procedimientos se declaran de manera similar a las funciones, excepto que no se necesita especificar ningún tipo de retorno.


Los procedimientos se declaran con esta sintaxis:

procedimiento nombre [(lista de parámetros)]
   <acciones>
fin_procedimiento

En este ejemplo pedimos al usuario que nos proporcione dos números enteros para calcular su suma y su producto usando procedimientos.

algoritmo Calcular_operaciones
var
   entero: a, b
inicio
   escribir ("Escribe el primer número: ")
   leer(a)
   escribir ("Escribe el segundo número: ")
   leer (b)
   suma(a,b)
   producto(a,b)
fin

procedimiento suma(entero a, entero b)
var
   entero: sum
inicio
   sum <- a + b
   escribir ("La suma de ambos números es: ", sum)
fin_procedimiento

procedimiento producto(entero a, entero b)
var
   entero: prod
inicio
   prod <- a * b
   escribir ("El producto de ambos números es: ", prod)
fin_procedimiento


Como podemos observar, cada uno de los procedimientos recibe como parámetros los dos números leídos en el programa principal. Con esos argumentos, cada procedimiento hace las operaciones solicitadas y muestra el resultado. Observa que el procedimiento no regresa ningún valor al programa principal, sino que la salida se muestra directamente dentro del cuerpo del procedimiento. Cuando el procedimiento suma(a,b) termina su labor, regresa el control al programa principal que, a su vez, llama al procedimiento producto(a,b) para que ejecute sus tareas. Cuando este termina regresa el flujo al cuerpo del programa principal para entonces terminar la ejecución.

En el ámbito de los lenguajes de programación actuales, tanto las funciones como los procedimientos son bastante utilizados en la forma de métodos dentro de clases, uno de los elementos de la programación orientada a objetos más utilizados en la actualidad. No vamos a profundizar mucho en este concepto, pero vale la pena mencionarlo para futuras referencias.

ACTIVIDADES DE APRENDIZAJE

  • Repite cada uno de los ejercicios de la lección anterior pero esta vez usa procedimientos en lugar de funciones para resolverlos.








martes, 9 de junio de 2015

Lógica de Programación (24) - Funciones



Ya en la lección anterior aprendimos que los subalgoritmos o subprogramas son una forma efectiva de atacar un problema complejo dividiéndolo en varios problemas más sencillos de resolver. En esta lección vamos a aprender una manera de lograr esta subdivisión a través del uso de funciones.

Si nos vamos a la definición matemática, una función es una operación que toma uno o más valores llamados argumentos y produce un valor determinado llamado resultado, que es el valor de la función para los argumentos dados. En la octava lección de este curso, aprendimos que en programación existen algunas funciones predeterminadas diseñadas para llevar a cabo cierta tarea o algún cálculo específico; para usarlas, sólo tenemos que "invocarlas" y pasarles algunos argumentos para obtener el resultado buscado. Por ejemplo, aprendimos que la función potencia(b,e) requiere de dos parámetros: base (b) y exponente (e), para regresar el resultado de elevar la base al exponente dado. Todas las funciones que aprendimos en aquella lección se llaman funciones internas porque ya están predefinidas en la mayoría de los lenguajes de programación. Sin embargo, también podemos desarrollar e invocar nuestras propias funciones, a las cuales se les conoce como funciones externas.

Cuando las funciones estándar no permiten realizar la función o cálculo deseado es necesario recurrir a las funciones externas que pueden ser definidas mediante una declaración de función, la cual tiene esta sintaxis:

<tipo_de_resultado> función <nombre_funcion> (lista de parametros o argumentos)
[declaraciones locales]
inicio
    <acciones> //cuerpo de la función
   devolver (<expresion>)
fin_función

En esta sintaxis vemos que debemos especificar varios componentes importantes:

  • El tipo de resultado que nos devuelve la función (si es entero, real, cadena, etc)
  • El nombre de la función con la que la vamos a invocar en nuestro algoritmo
  • La lista de parámetros o argumentos necesarios para la función. Esta lista debe tener un formato similar a este: (tipo_de_dato: argumento1, tipo_de_dato: argumento2...)
  • Las acciones que va a realizar la función con esos argumentos
  • La sentencia devolver, en la cual indicamos cuál es el resultado que va a entregar la función. Esta sentencia termina inmediatamente la función en la cual se ejecuta, por lo que siempre debe estar situada al final, justo antes del fin de la función. Es importante recordar también que una función sólo debe regresar un valor único.

Vamos a imaginar que la función potencia(b,e) no existe y tenemos que programarla nosotros mismos. El algoritmo completo de un programa que solicita al usuario los valores de la base y el exponente y luego muestra el resultado del cálculo, debería verse algo así:

algoritmo calcular_potencia
var
   real: base, exponente, res
inicio
   escribir("Introduce el valor de la base: ")
   leer(base)
   escribir("Introduce el exponente: ")
   leer(exponente)
   res <- potencia(base,exponente)
   escribir("El resultado calculado es: ", res)
fin

real función potencia(real b, real e)
var 
   real: resultado
   entero: i 
inicio
   resultado <- 1
   desde i<-1 hasta e hacer
      resultado <- resultado * b
   fin_desde
   devolver (resultado)
fin_función

Como puedes observar, el pseudocódigo correspondiente a la función potencia se encuentra localizado fuera del cuerpo del algoritmo principal (calcular_potencia). Sin embargo, ese código es ejecutado en el momento que se llama a la función por su nombre y se le pasan los parámetros correspondientes. Observa también que no es necesario que los nombres de las variables que se pasan como argumentos (base, exponente) sean iguales a las variables que recibirán dichos datos en la función (b,e). Cuando hagamos la invocación a la función, los valores se corresponderán en el mismo orden que los pasemos, es decir, el valor de base se asignará a b y el valor de exponente se asignará a e. Por supuesto, si queremos evitar usar tantos nombres distintos, podemos usar los mismos nombres para las variables tanto en la declaración de la función como en su llamada desde el programa principal. En ambos casos el resultado será el mismo. Finalmente, observa que una vez que se ejecuta el cuerpo de la función, se devuelve el valor almacenado en la variable resultado, el cual, al regresar al algoritmo principal, se almacena en la variable res, que es la que finalmente mostramos al usuario. En cierto sentido, para el programa principal, el funcionamiento de la función potencia es una caja negra, pues sólo le interesa que devuelva el resultado del cálculo sin importar cómo se hace.

Mira cómo se ve este ejemplo corriendo en C#:



Ahora veamos otro ejemplo. En el siguiente algoritmo, creamos un programa que utiliza una función para calcular el factorial de un número dado por el usuario. Por si no lo recuerdas, el factorial de un número n, representado como n!, es igual al producto de todos los números que están por debajo de n, incluyendo a este último. Es decir: n! = n * (n-1) * (n-2) * (n-3) ...

entero función factorial (entero n)
var
   entero: i, f
inicio
   f <- 1
   desde i<-1 hasta n hacer
      f <- f * i
   fin_desde
   devolver (f)
fin_función

algoritmo calcular_factorial
var
   entero: n
inicio
   escribir("Dame un número entero: ")
   leer(n)
   escribir("El factorial de ", n, " es igual a ", factorial(n))
fin

Si te fijas bien, en este listado el código de la función está antes del algoritmo principal. Esto también es perfectamente válido (de hecho, algunos lenguajes de programación exigen que se declaren primero las funciones, antes del programa principal). También observa que en este caso no asignamos el resultado de la invocación de la función a una variable, sino que directamente estamos escribiendo el resutlado sin usar una variable intermedia. El usuario nunca verá en pantalla el texto "factorial(n)", sino que verá el resultado del cálculo junto con el resto del mensaje que le estamos mostrando.

Mira cómo funciona este programa en C#:



En este último ejemplo creamos no una sino dos funciones que se llaman mutuamente y trabajan en conjunto para calcular el área de un polígono de n lados.

algoritmo area_poligono
var
   real: n, lado, ap, area
inicio
   escribir("¿Cuántos lados tiene el polígono? ")
   leer (n)
   escribir("¿Cuál es la medida de esos lados? ")
   leer(lado)
   escribir("¿Cuánto mide el apotema? ")
   leer(ap)
   area = area_poligono(n, lado, ap)
   escribir("El área del polígono es de ", area)
fin

real función area_poligono(real n, real lado, real ap)
var
   real: area
inicio
   area = perimetro(n, lado) * ap
   devolver (area)
fin_función

real función perimetro(real n, real lado)
inicio
   devolver n * lado
fin_función

Observa cómo funciona este código en C#:



En estos ejemplos ha quedado en evidencia la utilidad de las funciones como un medio para dividir un problema complejo en pequeños problemas más fáciles de resolver y así llegar a una solución a dicho problema. Sin embargo, las funciones tienen una limitación importante: sólo pueden devolver un valor a la vez. ¿Qué pasa si necesito devolver dos o más valores? Para eso existen los procedimientos, los cuales vamos a estudiar en la siguiente lección. ¡Hasta la próxima!

ACTIVIDADES DE APRENDIZAJE


  • Escribe un algoritmo que use una función creada por tí para encontrar el mayor de dos números enteros dados por el usuario.
  • Diseña una función booleana llamada Digito que determine si un caracter dado por el usuario es uno de los dígitos del 0 al 9
  • Escribe una función que transforme un número entero dado en su correspondiente número romano (nota: limita los números a convertir al rango de 1 a 3999 únicamente)


Enlace: Índice del curso




Copyright © Mi rincón del código Compartido por Gooyaabi Templates | Powered By Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com