Posiblemente la función aritmética más básica que aprendemos a manejar de niños es la suma.
La función +, en su formato infijo, recibe como argumentos dos valores numéricos, y por eso escribimos, por ejemplo, 4 + 5.
Y si usamos una notación funcional, como la que ya estudiamos en capítulos anteriores, podríamos escribir +(4, 5).
El resultado será el número entero positivo 9.
Las funciones son generalizaciones que tienen como propósito usar datos de entrada, y un proceso interno que usa dichos datos de entrada para generar como resultado datos de salida.
La generalización radica en el hecho que los datos de entrada pueden variar para así obtener distintos resultados.
En computación, Las funciones son la unidad fundamental de encapsulamiento de las tareas del software. La idea detrás de las funciones es hacer un trabajo a partir de unos datos de entrada para obtener datos de salida, sin importar cómo queden estructuradas internamente.
Lo importante cuando nos enfrentamos a una función es saber qué hace, y no cómo lo hace.
El cómo es su estructura interna, sus etapas o pasos para llegar al resultado, y normalmente se le ve como una caja negra.
El qué es su objetivo, tarea o función.
Por ejemplo, la función seno que usamos en nuestras calculadoras; a ti te importa lo que hace la función, pero no te importa cómo está estructurada dicha función internamente para lograr el resultado. Si sacas el seno(30), te importa solo su resultado: 0.5.
También se asume aquí que todo algoritmo deberá formar parte de una función, y una función podrá apoyarse en otras para lograr hacer su trabajo. Esto es, no hay pseudocódigo que no pertenezca a una función, ya que hasta el código más desordenado tiene un propósito y unos datos de entrada.
La función +, en su formato infijo, recibe como argumentos dos valores numéricos, y por eso escribimos, por ejemplo, 4 + 5.
Y si usamos una notación funcional, como la que ya estudiamos en capítulos anteriores, podríamos escribir +(4, 5).
El resultado será el número entero positivo 9.
Las funciones son generalizaciones que tienen como propósito usar datos de entrada, y un proceso interno que usa dichos datos de entrada para generar como resultado datos de salida.
La generalización radica en el hecho que los datos de entrada pueden variar para así obtener distintos resultados.
En computación, Las funciones son la unidad fundamental de encapsulamiento de las tareas del software. La idea detrás de las funciones es hacer un trabajo a partir de unos datos de entrada para obtener datos de salida, sin importar cómo queden estructuradas internamente.
Lo importante cuando nos enfrentamos a una función es saber qué hace, y no cómo lo hace.
El cómo es su estructura interna, sus etapas o pasos para llegar al resultado, y normalmente se le ve como una caja negra.
El qué es su objetivo, tarea o función.
Por ejemplo, la función seno que usamos en nuestras calculadoras; a ti te importa lo que hace la función, pero no te importa cómo está estructurada dicha función internamente para lograr el resultado. Si sacas el seno(30), te importa solo su resultado: 0.5.
También se asume aquí que todo algoritmo deberá formar parte de una función, y una función podrá apoyarse en otras para lograr hacer su trabajo. Esto es, no hay pseudocódigo que no pertenezca a una función, ya que hasta el código más desordenado tiene un propósito y unos datos de entrada.
FUNCIONES
Los programas de computadora se construyen a base de crear redes cooperativas de funciones.
Una red cooperativa es un conjunto disperso de funciones se conecta entre si para llevar a cabo tareas más amplias (como cultura general, las funciones es mejor que cooperen, y no que colaboren. Colaborar es indicio de cohesión alta, algo que se debe evitar en desarrollo de software- Esto es solo lenguaje, y no tiene porqué preocuparnos).
Cada una de las funciones de un programa deberá encargarse de hacer una tarea muy concreta.
A esto se le conoce en diseño de software como el principio de responsabilidad única, o PRU.
De hecho, si hay una función que es capaz de hacer muchas cosas, es mejor reescribirla como varias funciones, cada una encargada de hacer una sola cosa.
Esto también ayuda a que las funciones sean pequeñas, sean más legibles, sean fáciles de modificar, y que, de ser necesario, sea relativamente fácil volverlas a hacer.
También se espera que las funciones puedan ser reemplazadas fácilmente por otras funciones.
Una red cooperativa es un conjunto disperso de funciones se conecta entre si para llevar a cabo tareas más amplias (como cultura general, las funciones es mejor que cooperen, y no que colaboren. Colaborar es indicio de cohesión alta, algo que se debe evitar en desarrollo de software- Esto es solo lenguaje, y no tiene porqué preocuparnos).
Cada una de las funciones de un programa deberá encargarse de hacer una tarea muy concreta.
A esto se le conoce en diseño de software como el principio de responsabilidad única, o PRU.
De hecho, si hay una función que es capaz de hacer muchas cosas, es mejor reescribirla como varias funciones, cada una encargada de hacer una sola cosa.
Esto también ayuda a que las funciones sean pequeñas, sean más legibles, sean fáciles de modificar, y que, de ser necesario, sea relativamente fácil volverlas a hacer.
También se espera que las funciones puedan ser reemplazadas fácilmente por otras funciones.
Estructura de una función
Todo lenguaje de programación trae consigo un conjunto de funciones que serán útiles para construir un programa. También necesitamos construir nuestras propias funciones, y, para el efecto, necesitamos algunas palabras especiales que nos permitan definirlas.
Las funciones de aritmética, como la suma, que expresamos, por ejemplo como θ + λ, podemos expresarlas en algunos lenguajes en forma funcional como +( θ , λ). Las funciones de nuestros algoritmos, escritos en pseudocódigo, tendrán una notación funcional también.
En general, casi todos los lenguajes permiten definir la función con alguna palabra clave, un nombre de función, y a continuación, normalmente entre paréntesis, la lista de parámetros de la función. En el caso de los pseudocódigos, existirán varias formas de definir funciones.
La primera es la definición de función bloque, que la podremos definir de dos formas, y la segunda es la definición de función expresión.
Las funciones de aritmética, como la suma, que expresamos, por ejemplo como θ + λ, podemos expresarlas en algunos lenguajes en forma funcional como +( θ , λ). Las funciones de nuestros algoritmos, escritos en pseudocódigo, tendrán una notación funcional también.
En general, casi todos los lenguajes permiten definir la función con alguna palabra clave, un nombre de función, y a continuación, normalmente entre paréntesis, la lista de parámetros de la función. En el caso de los pseudocódigos, existirán varias formas de definir funciones.
La primera es la definición de función bloque, que la podremos definir de dos formas, y la segunda es la definición de función expresión.
Forma detallada función bloque
La primera forma para definir una función hace explícitos los parámetros y los valores de salida por medio del uso de la palabra in y out :
fx | fn | fun | funct |function nombrefunción
in[:] lista de parámetros,…
[valid|check] [:] lista de condiciones
out[:] [variable]::Tipo valor retorno
[begin | start | { ]
cuerpo de la función y retorno de valor
end | finish | }
La palabra function, podrá ser reemplazada si se quiere por alguna entre fx, fn, fun, funct, es la forma en que en pseudocódigo indicaremos que vamos a definir una función.
A continuación, viene la palara in (seguida de dos puntos), que nos permite definir la lista de parámetros o datos de entrada de la función. Un dato de entrada lleva nombre y tipo. La lista de parámetros se separa con comas.
Luego viene la palabra valid o check, donde podemos poner una condición o condiciones separadas por coma. Al menos una de las condiciones debe ser cierta, para que se pueda ingresar a la función. Si ninguna condición es cierta, la función no corre y se emite un error.
Luego viene la palabra out donde especificaremos un tipo, o una variable con tipo que contendrá el valor que será devuelto por la función. Si no se especifica la variable, se deberá especificar al menos el tipo de la función. Si la variable se especifica, esa será la variable de la que se extraerá el valor de retorno cuando la función llegue a su fin y se alcance la palabra end (o cualquier variante).
Ya podemos comenzar a escribir el algoritmo, y podemos usar la palabra begin o start de forma opcional, para especificar que comenzará el trabajo de la función.
La palabra begin también la podemos reemplazar por el símbolo llave izquierda (‘left curly bracket’ en inglés) de apertura. Si se usa el símbolo y no la palabra begin, es recomendable que la palabra end se reemplace por el símbolo llave derecha (’right curly bracket’ en inglés).
La palabra end cierra la función y podrá llevar opcionalmente uno de los sufijos function, fx, fn, fun, funct según sea su preferencia al escribir. Basta con poner end o finish, pero a veces deseamos documentar mejor el fin de la función.
fx | fn | fun | funct |function nombrefunción
in[:] lista de parámetros,…
[valid|check] [:] lista de condiciones
out[:] [variable]::Tipo valor retorno
[begin | start | { ]
cuerpo de la función y retorno de valor
end | finish | }
La palabra function, podrá ser reemplazada si se quiere por alguna entre fx, fn, fun, funct, es la forma en que en pseudocódigo indicaremos que vamos a definir una función.
A continuación, viene la palara in (seguida de dos puntos), que nos permite definir la lista de parámetros o datos de entrada de la función. Un dato de entrada lleva nombre y tipo. La lista de parámetros se separa con comas.
Luego viene la palabra valid o check, donde podemos poner una condición o condiciones separadas por coma. Al menos una de las condiciones debe ser cierta, para que se pueda ingresar a la función. Si ninguna condición es cierta, la función no corre y se emite un error.
Luego viene la palabra out donde especificaremos un tipo, o una variable con tipo que contendrá el valor que será devuelto por la función. Si no se especifica la variable, se deberá especificar al menos el tipo de la función. Si la variable se especifica, esa será la variable de la que se extraerá el valor de retorno cuando la función llegue a su fin y se alcance la palabra end (o cualquier variante).
Ya podemos comenzar a escribir el algoritmo, y podemos usar la palabra begin o start de forma opcional, para especificar que comenzará el trabajo de la función.
La palabra begin también la podemos reemplazar por el símbolo llave izquierda (‘left curly bracket’ en inglés) de apertura. Si se usa el símbolo y no la palabra begin, es recomendable que la palabra end se reemplace por el símbolo llave derecha (’right curly bracket’ en inglés).
La palabra end cierra la función y podrá llevar opcionalmente uno de los sufijos function, fx, fn, fun, funct según sea su preferencia al escribir. Basta con poner end o finish, pero a veces deseamos documentar mejor el fin de la función.
Forma común de una función bloque
La otra forma de definir una función bloque es más similar a los lenguajes de programación en cuanto a que los parámetros de la función se delimitan entre paréntesis al lado del nombre de la función, y los tipos de salida van también en la cabecera al final:
fx|fn|fun|funct|function nomb (parámetros,…)[vars]::Tipos
[valid|check] [:] lista de condiciones prerequisitos
[begin | start | {]
cuerpo de la función
[return] [valor]
[end | finish | }]
En este libro y por razones didácticas, se comenzará con el uso de la primera forma que es más detallada, y genera mayor comprensión, pero poco a poco también incorporaremos la segunda forma. Con una definición de función de bloque se crean bloques de código de cualquier naturaleza. No hay restricciones en cuanto a las posibilidades.
fx|fn|fun|funct|function nomb (parámetros,…)[vars]::Tipos
[valid|check] [:] lista de condiciones prerequisitos
[begin | start | {]
cuerpo de la función
[return] [valor]
[end | finish | }]
En este libro y por razones didácticas, se comenzará con el uso de la primera forma que es más detallada, y genera mayor comprensión, pero poco a poco también incorporaremos la segunda forma. Con una definición de función de bloque se crean bloques de código de cualquier naturaleza. No hay restricciones en cuanto a las posibilidades.
Estructura de una función expresión
Otra forma de definir funciones es la definición de función expresión, que se usa solo para llevar expresiones a funciones:
[fx|fn|fun|funct|function ] nombrefunción(parámetros,…)[Tipo] = expresión
o simplemente
nombrefunción(parámetros,…)[Tipo] = expresión
Con esta alternativa, solo se permiten expresiones como cuerpo de la función, y el valor de retorno es el valor de la expresión.
Note la opcionalidad de las palabras fx, fn, fun, funct, function, pues en este caso no son necesarias.
Todos los lenguajes de programación contienen algún tipo de sintaxis para definir su tipo de función de bloque, pero no todos los lenguajes permiten sintaxis para definición de funciones de expresión.
También están las funciones anónimas, otra forma de definir funciones expresión, también llamadas funciones lambda, y que revisaremos más adelante.
[fx|fn|fun|funct|function ] nombrefunción(parámetros,…)[Tipo] = expresión
o simplemente
nombrefunción(parámetros,…)[Tipo] = expresión
Con esta alternativa, solo se permiten expresiones como cuerpo de la función, y el valor de retorno es el valor de la expresión.
Note la opcionalidad de las palabras fx, fn, fun, funct, function, pues en este caso no son necesarias.
Todos los lenguajes de programación contienen algún tipo de sintaxis para definir su tipo de función de bloque, pero no todos los lenguajes permiten sintaxis para definición de funciones de expresión.
También están las funciones anónimas, otra forma de definir funciones expresión, también llamadas funciones lambda, y que revisaremos más adelante.
Tips de dato de retorno de una función
Toda función tiene valor de retorno.
Debido a que todo dato tiene un tipo asociado, tanto los datos de entrada (valores de los parámetros) que se definen con la palabra in como los datos de salida (valor de retorno) que se definen con la palabra out, tendrán asociado un tipo de dato.
No es posible pensar en un dato que no tenga un tipo asociado, sea este simple o compuesto.
En algunos lenguajes es obligatorio definir el tipo de los parámetros, mientras que en otros es opcional. Incluso hay lenguajes a los que no es posible hacerle una declaración explicita de los tipos de los datos, y toda la tipificación es completamente dinámica.
En nuestros pseudocódigos, el tipo de las funciones y de los parámetros puede ser especificado, y se recomienda hacerlo para mayor claridad de los datos de entrada y salida que se esperan.
Las entradas (parámetros) y salidas (valor de retorno) de una función, pueden ser de cualquiera de los tipos simples ya estudiados ::Num, ::R, ::Q, ::Z, ::N, ::C, ::Bool, ::Char, ::String, o datos compuestos ::Tuple, ::Struct o ::Array.
Las salida out podrá estar asociadas a nombres de variables, y si al momento de terminar una función hay [ret[urn]] desnudo sin valor o simplemente no hay [ret[urn]], se usan los valores de estas variables como los valores de retorno de la función.
Si no hay ni variables out, ni return, la función es un procedimiento y no devuelve valor, o, por lo menos, devuelve valor Vacío, algo que podríamos expresar como Φ.
El valor de retorno Φ es útil para expresar procedimientos, que son una especie de funciones sin valor, pero con efectos colaterales.
Uso de funciones existentes
Al uso de una función, se le conoce en programación como 'llamada'.
Para usar una función que ya se encuentre definida, se deberá hacer referencia a su nombre, y entre paréntesis, pondremos los valores con los que queremos invocar la función.
Por ejemplo, en el caso de +(θ , λ), + es el nombre de la función, y θ y λ son argumentos o valores para los parámetros de la función.
El valor resultante podría ser usado dentro de cualquier expresión. Ahora suponga que el nombre de la función no es el signo +, sino algo más textual: sumar. En realidad, se trata de lo mismo, y se escribiría
sumar(θ , λ)
Esta función podría ser usada dentro de otras expresiones.
Por ejemplo,
x + w + sumar(θ , λ)
Así como x y w son valores a ser usados en la expresión, la llamada sumar(θ , λ) también dará como resultado un valor que podrá ser usado en la expresión.
Incluso podríamos tener llamadas de funciones como argumentos, o cualquier otro tipo de expresiones, como, por ejemplo
sumar( x, sumar( w, sumar(θ , λ) ) )
Esta expresión da como resultado lo mismo que la anterior, al igual que:
sumar( sumar(x, w) , sumar(θ , λ) )
Para usar una función que ya se encuentre definida, se deberá hacer referencia a su nombre, y entre paréntesis, pondremos los valores con los que queremos invocar la función.
Por ejemplo, en el caso de +(θ , λ), + es el nombre de la función, y θ y λ son argumentos o valores para los parámetros de la función.
El valor resultante podría ser usado dentro de cualquier expresión. Ahora suponga que el nombre de la función no es el signo +, sino algo más textual: sumar. En realidad, se trata de lo mismo, y se escribiría
sumar(θ , λ)
Esta función podría ser usada dentro de otras expresiones.
Por ejemplo,
x + w + sumar(θ , λ)
Así como x y w son valores a ser usados en la expresión, la llamada sumar(θ , λ) también dará como resultado un valor que podrá ser usado en la expresión.
Incluso podríamos tener llamadas de funciones como argumentos, o cualquier otro tipo de expresiones, como, por ejemplo
sumar( x, sumar( w, sumar(θ , λ) ) )
Esta expresión da como resultado lo mismo que la anterior, al igual que:
sumar( sumar(x, w) , sumar(θ , λ) )
EJEMPLO.
Determinar el valor de cada variable a medida que el algoritmo avanza. R/ θ <- 2 λ <- θ ^ 2 # λ es 4 Ψ <- θ * λ # Ψ es 8 x <- sumar( θ , λ ) # x es 6 w <- sumar( sumar(x, λ) / 2 , x / 2 ) # w es 8 Expliquemos cómo se obtuvo w en el ejemplo anterior.
Primero se llevó a cabo las operaciones más internas, sumar(x, λ) /2 que da 5, y x/2 que da 3. Finalmente la expresión reducida sumar(5,3) se reduce a 8. Por lo tanto, w es 8. Los paréntesis, como se puede ver, siguen siendo muy dominantes en prioridad, y las expresiones internas se reducen primero para poder usarlas en las más externas. Ya sabemos cómo podemos referirnos a una función para usarla. Volveremos al tema de llamadas más adelante, pero en contextos más interesantes. Ahora necesitamos es aprender a construir el interior de nuestras funciones para nuestros algoritmos. |
FUNCIONES SIN BIFURCACIONES
La lógica algorítmica es una habilidad que se alcanza haciendo ejercicios. No es una técnica que se aprende desde la teoría. Se deben hacer muchos ejercicios para conseguir adquirir una lógica algorítmica y de programación. Por tal motivo, la estrategia de este libro se basa en mucho ejemplo, pero, sobre todo, muchos ejercicios.
A continuación, comenzaremos con algunos ejercicios que, dicho sea de paso, debes entender. No basta con ver. También se necesita entender y hacer, para generar una lógica propia que te permita llevar soluciones a un computador.
Las soluciones que no se bifurcan son totalmente lineales: esto es, el problema se soluciona de una única forma siempre, lo que implica que no hay condiciones que permitan tomar decisiones dentro del código.
Recuerde: cada problema exigirá datos de entrada y datos de salida, y usted deberá ser tan estricto como sea posible en cuanto a los tipos de los datos.
A continuación, comenzaremos con algunos ejercicios que, dicho sea de paso, debes entender. No basta con ver. También se necesita entender y hacer, para generar una lógica propia que te permita llevar soluciones a un computador.
Las soluciones que no se bifurcan son totalmente lineales: esto es, el problema se soluciona de una única forma siempre, lo que implica que no hay condiciones que permitan tomar decisiones dentro del código.
Recuerde: cada problema exigirá datos de entrada y datos de salida, y usted deberá ser tan estricto como sea posible en cuanto a los tipos de los datos.
EJEMPLO: Distancia al origen
Construya una función que, dados un par de valores (x,y) que corresponden a un punto en el plano cartesiano, determine la distancia al origen. Asigne valores al par coordenado. Asuma continuidad de valores entre las instrucciones. La distancia al origen para un punto en el plano cartesiano es la raíz cuadrada de la suma de los cuadrados de x y de y. R/ function distanciaorigen in x::R , y::R out ::R+ begin return (x^2 + y^2) ^ 0.5 end En el ejemplo que se presentó, la función distanciaorigen recibe como datos de entrada los valores x , y. Los datos de entrada son datos reales, y solo habrá un dato de salida que es la distancia.
Dado que al momento de devolver el valor no se usó una variable, solo hace falta definir el tipo en la sección out. |
Ahora supongamos que queremos construir una función en papercode que me permita determinar si tres lados dados me permiten construir un triángulo equilátero.
Necesitamos un nombre para nuestra función. Un buen nombre se basa en lo que efectivamente hace la función. Como la función va a indagar si un triángulo es equilátero, podríamos llamarla esEquilátero.
Dado que necesitamos como datos de entrada los tres lados, esos serán los parámetros de la función y los llamaremos a modo de ejemplo, ladoA, ladoB y ladoC. El nombre es arbitrario, y puede ser cualquiera. Como los lados son valores numéricos, diremos que son de tipo real ::R+. La estructura para los parámetros es ponerlos entre paréntesis y separados por el coma (,).
Debido a que la función debe decirnos si sí es equilátero o no lo es, eso lo podemos representar con un valor de tipo booleano ::Bool que nos represente los dos estados posibles que queremos, .t. o .f.
Necesitamos un nombre para nuestra función. Un buen nombre se basa en lo que efectivamente hace la función. Como la función va a indagar si un triángulo es equilátero, podríamos llamarla esEquilátero.
Dado que necesitamos como datos de entrada los tres lados, esos serán los parámetros de la función y los llamaremos a modo de ejemplo, ladoA, ladoB y ladoC. El nombre es arbitrario, y puede ser cualquiera. Como los lados son valores numéricos, diremos que son de tipo real ::R+. La estructura para los parámetros es ponerlos entre paréntesis y separados por el coma (,).
Debido a que la función debe decirnos si sí es equilátero o no lo es, eso lo podemos representar con un valor de tipo booleano ::Bool que nos represente los dos estados posibles que queremos, .t. o .f.
EJEMPLO: Triángulo equilátero
Escribir una función en pseudocódigo papercode que pueda ser usada para determinar si un triángulo es equilátero R/ fx esequilátero in ladoa:: R+, ladob:: R+, ladoc:: R+ out ::Bool begin … #aquí va el algoritmo en pseudocódigo papercode… … end EXPLICACIÓN. Primero viene alguna de las palabras especiales para abrir la función. Como ya vimos, se trata de alguna de las palabras fx,fn,fun,func o function. Luego el nombre de la función, en este caso esEquilatero, que denota el propósito de la función. A continuación del nombre escribimos los datos de entrada, que se llaman parámetros de la función. Esto lo hacemos usando la palabra in. Finalmente, escribimos el tipo del valor que devuelve la función. Como la función nos dice si el triángulo es equilátero, la función devuelve el valor .t. o .f.según sea el caso de si el triángulo es o no es equilátero. Entonces, el tipo será Bool. El tipo de valor se da en la sección out. El algoritmo lo enmarcamos entre las palabras begin y end. Sin embargo,usar begin es opcional, pero es importante usar la palabra end al final. Cuando terminemos la función, la cerraremos con alguna de las palabras de cierre, según nuestras preferencias: end, endfx, endfn, endfun, endfunct oendfunction. Nos falta el algoritmo. En el capítulo sobre expresiones, se vio que una expresión booleana nos permite hacer construcciones especiales para obtener un valor .t o .f. según sea el caso. Para que la función indique si es o no un triángulo equilátero, solo basta con relacionar los lados. Una expresión que nos resuelve el problema es comparar los tres lados como ladoA .EQ. ladoB ∧ ladoB .EQ. ladoC Esta expresión comparan el lado A con el lado B, y si son iguales, compara el lado B con el lado C. (Ver cortocircuito en el capítulo de expresiones, operador lógico conjuntivo.) si las dos condiciones son ciertas, el valor de la expresión se reduce a .t. Podemos escribir dentro de la función la expresión planteada, y ese es el valor que deberá devolver la función. Para que una función devuelva su valor, usaremos la palabra especial ret, o return según sea nuestra preferencia, en cualquier lugar del código que sea conveniente según la lógica de programación empleada. Sin embargo, en nuestros pseudocódigos, si el valor de retorno es la última instrucción que se ejecutará en la función antes de end o endfunction, puede omitirse la palabra return. Adicionalmente, si el valor de retorno es una variable definida en la sección out, usar retorno al finalizar el algoritmo es sólo cuestión de gusto y legibilidad, ya que la variable definida en out da por sentado el valor salida. La palabra ret o return se puede usar en cualquier parte del código en el que se desee devolver un valor para dar por finalizada la función. Luego de que la instrucción return ha sido ejecutada, no hay nada más que hacer en la función. Esto también es cierto para algunos lenguajes de programación, por lo que podremos también seguir esta estrategia en nuestros seudocódigos con papercode. Sin embargo, para efectos de claridad, es mejor incluirla. fx esEquilátero in ladoA::R+, ladoB::R+, ladoC::R+ out equi::Bool begin equi <- ladoA .EQ. ladoB ∧ ladoB .EQ. ladoC end equi es una variable de tipo booleano que usamos para almacenar el valor, y ese valor finalmente lo usamos para devolverlo como valor de la función al estar definido en sentencia out. Sin embargo, podemos devolver el valor directamente y evitar el uso de una variable intermedia equi, ya que la variable finalmente no será usada: fx esEquilátero in ladoA::R+, ladoB::R+, ladoC::R+ out ::Bool begin return ladoA = ladoB ∧ ladoB = ladoC end Dado que la palabra return es opcional al momento de devolver el valor, podría haberse escrito la función como: fx esEquilátero in ladoA::R+, ladoB::R+, ladoC::R+ out ::Bool begin ladoA .EQ. ladoB ∧ ladoB .EQ. ladoC. #.EQ. es operador relacional de igualdad End La forma en que usted estructure su algoritmo es totalmente flexible. Por ejemplo, podríamos guardar cada comparación en variables independientes, así: fx esEquilátero in ladoA,::R+, ladoB::R+, ladoC::R+ out ::Bool begin compareAB ::Bool <- ladoA = ladoB compareBC ::Bool <- ladoB = ladoC return compareAB ∧ compareBC end Cuál es la estrategia que debemos usar para escribir nuestras expresiones, se convierte meramente en un tema de gusto, ya que todas las versiones presentadas resuelven el problema con la misma eficiencia relativa. EJEMPLO: Volumen de una esfera
Escriba una función en pseudocódigo papercode que permita calcular el volumen de balones de playa, conocido su radio. El balón de playa s considera esférico. R/ Se debe entender que qué se pretende resolver, y qué datos están disponibles. En este caso, tenemos el radio como dato de entrada, que es de tipo ::R+, y el volumen como dato de salida, que también tipo ::R+. El volumen de una esfera puede ser calculado por la ecuación. Esto es suficiente para reconocer tanto los tipos de datos de entrada como el tipo de dato de salida de la función: fx volumenEsfera in radio::R+ out volumen::R+ begin pi <- 3.14159265358 #se infiere tipo ::R+ volumen <- 4/3*pi^3 #se infiere tipo ::R+ end Por supuesto, una versión sin el uso de variables sería: fx volumenEsfera in radio::R+ out ::R+ begin return 4/3*3.14159265358 ^ 3 endfx EJEMPLO: Balones alineados
Escriba una función en pseudocódigo papercode que le permita calcular cuántos balones de un diámetro concreto en centímetros dado como dato de entrada, se pueden filar en un lugar cualquiera de longitud L dada como parámetro en km. R/ fx cuantosBalones in diámatroBalón::R+ , distanciaLugar::R+ out ::R+ begin ret distanciaLugar / diámatroBalón end Elegir el tipo de función como ::R+ se debe a que la división va a manejar fracciones. EJEMPLO: Hipotenusa
Escriba una función en pseudocódigo papercode que le permita calcular la hipotenusa de un triángulo rectángulo conocidos los puntos en el plano cartesiano (x1, y1) y (x2, y2) que forman su hipotenusa. fx hipotenusa in x1::R+ , y1::R+ , x2::R+ , y2::R+ out hipo::R+ begin catAd::R+ <- x2 – x1 catOp::R+ <- y2 – y1 hipo <- (catOp * catOp + catAd * catAd) ^ 0.5 end EXPLICACIÓN. La hipotenusa se calcula por teorema de Pitágoras, que nos dice que se toman las diferencias de las x (x2 y x1), luego las diferencias de las y (y2 y y1), y cada diferencia es elevada al cuadrado. Luego sumamos las dos diferencias, y a ese resultado le calculamos la raíz cuadrada. EJEMPLO: Extraer dígito menos significativo
Escriba una función en pseudocódigo papercode que permita hallar el dígito menos significativo de la parte entera de un número real positivo. Por ejemplo, dado el número 473.98, la función entrega 3. Si el número no tiene parte entera, la función entrega cero. R/ Una estrategia es obtener la parte entera, y a la parte entera extraer el 3 con una operación módulo que nos de el residuo. Ejemplo: la parte entera de 473.98 es 473 y a esa parte entera le extraemos el dígito menos significativo que es 3 fx dígitoMenosSignifParteEntera in num::R+ out dig::N begin entero <- ::Z(num) dig <- entero % 10 end EXPLICACIÓN. Supongamos el número dado, 473.98. La respuesta esperada es 3. Para hallar el dígito menos significativo de la parte entera, podemos extraer la parte entera del número. Eso lo podemos lograr convirtiendo el número real a tipo entero usando un casting de tipo ::Z(473.98), lo que nos deja con el valor 473, y almacenamos ese valor en una nueva variable. Podemos hallar el residuo de dividir dicho entero por 10. Esto es, al dividir el número 473 entre 10, se obtiene como resultado 47 como cociente, y 3 como residuo. El número 3, es el dígito buscado. |
ESTRUCTURAS DE DATOS COMPUESTOS
Los tipos de datos no siempre van a ser simples, y podrían ser compuestos. La gran mayoría de las funciones, en realidad, van a requerir de estructuras más complejas que simples variables pasadas como parámetros. De hecho, es recomendable empacar, de forma cohesiva, datos relacionados dentro de una misma estructura, para facilitar el trabajo con dichos datos relacionados.
Las estructuras que usaremos en pseudocódigos, serán las tuplas, las estructuras o registros, y los arreglos.
Como es de esperarse, la única forma de comprender cómo usar estos elementos, es a partir de ejemplos.
Las estructuras que usaremos en pseudocódigos, serán las tuplas, las estructuras o registros, y los arreglos.
Como es de esperarse, la única forma de comprender cómo usar estos elementos, es a partir de ejemplos.
EJEMPLO: Multiplicar dos matrices de 2x2 en estructuras
Escriba una función en pseudocódigo que reciba dos matrices A y B cuadradas de 2x2 que se pasan como estructuras, y halle el producto matricial | a b | | e f | A = | c d | , B = | g h | El producto matricial se determina así: | ae + bg af + bh | C = AB = | ce + dg cf + dh | R/ Como los datos pasan como estructuras, podríamos estructurar un tipo que represente una matriz de 2x2, que contenga cuatro elementos. Ese tipo sería usado por los parámetros. struct Matriz2x2 e11::R e12::R e11::R e12::R end fx multmat in A:: Matriz2x2, B:: Matriz2x2 out C:: Matriz2x2 begin C.e11 <- A.e11 x B.e11 + A.e12 x B.e21 C.e12 <- A.e11 x B.e12 + A.e12 x B.e22 C.e21 <- A.e21 x B.e11 + A.e22 x B.e21 C.e21 <- A.e21 x B.e12 + A.e22 x B.e22 end Note que el tipo estructura se define una sola vez. Luego podemos tener cuantas variables tengamos de ese tipo. También note que, para referenciar un elemento, se usa notación con punto(.) indicando el nombre del elemento. Usamos estructuras para conocer cómo se usan. Sin embargo, este tipo de problemas con matrices es mejor estructurarlo con el tipo de dato Array, ya que, para arreglos más grandes, necesitaremos usar bucles que recorran todos los elementos de forma algorítmica más automática. EJEMPLO: Multiplicar dos matrices de 2x2 en estructuras
Escriba una función en pseudocódigo que reciba dos matrices A y B de 2x2 que se pasan como arreglos, y halle el producto matricial como se explicó en el ejemplo anterior: R/ En ese caso, usamos directamente el tipo de dato Array. Note que en los parámetros se especifica que son arreglos de dos dimensiones. fx multmat in A [2,2]::R, B [2,2]::R out C [2,2]::R begin C[1,1] <- A[1,1] x B[1,1] + A[1,2] x B[2,1] C[1,2] <- A[1,1] x B[1,2] + A[1,2] x B[2,2] C[2,1] <- A[2,1] x B[1,1] + A[2,2] x B[2,1] C[2,1] <- A[2,1] x B[1,2] + A[2,2] x B[2,2] ret C end El uso de matrices es muy poderoso, y su uso puede ser simplificado muchísimo con estructuras cíclicas, como veremos en sectores posteriores del texto. |
FUNCIÓN EXPRESIÓN
Muchos de los ejemplos que hemos presentado pueden ser llevados a una única expresión. Cuando las expresiones son sencillas, esto es, son cortas y pueden leerse con facilidad, es recomendable definir funciones en su forma de función expresión.
Para definir una función expresión, solo damos un nombre de función con sus parámetros, e igualamos a la expresión, como se explicó previamente cuando se habló de los tipos de las formas en que podemos estructurar funciones en pseudicódigos.
Una función expresión es simple de escribir, y sólo pretende, como su nombre lo indica, definir una expresión:
nombrefunción(parámetros opcionalmente tipificados,…)[Tipo] = expresión
Note, sin embargo, la opcionalidad de la palabra fx, o function, o func, ya que, en este caso, no es necesaria. La expresión puede ser cualquiera, siempre que se respete el tipo de la función.
Algunos de los ejemplos que se muestran, ya fueron resueltos en una versión de definición de función bloque. Sin embargo, es mejor tener estas funciones con una definición de función expresión
Para definir una función expresión, solo damos un nombre de función con sus parámetros, e igualamos a la expresión, como se explicó previamente cuando se habló de los tipos de las formas en que podemos estructurar funciones en pseudicódigos.
Una función expresión es simple de escribir, y sólo pretende, como su nombre lo indica, definir una expresión:
nombrefunción(parámetros opcionalmente tipificados,…)[Tipo] = expresión
Note, sin embargo, la opcionalidad de la palabra fx, o function, o func, ya que, en este caso, no es necesaria. La expresión puede ser cualquiera, siempre que se respete el tipo de la función.
Algunos de los ejemplos que se muestran, ya fueron resueltos en una versión de definición de función bloque. Sin embargo, es mejor tener estas funciones con una definición de función expresión
EJEMPLO.
Construya una función expresión que, dados un par de valores x,y que corresponden a un punto en el plano cartesiano, determine la distancia al origen. R/ distanciaorigen(x::R, y::R)::R+ = (x^2 + y^2) ^ 0.5 EJEMPLO.
Escriba en pseudocódigo una función expresión para calcular el desplazamiento vertical en metros de un objeto usando la expresión x0 + v0 + gt^2/2, donde x0, v0, y t, corresponden, respectivamente, a la posición inicial en metros, la velocidad inicial en m/s, y el tiempo transcurrido en segundos; g corresponde a la gravitación terrestre de 9.80665 m.s^2. R/ desplazamiento(x::R, v::R, t::R)::R+ = x+v*t+(9.806*t^2)/2 COMPARACIÓN. Comparamos con su equivalente función bloque, tendremos una sintaxis más verbosa: function desplazamiento in x::R, v::R, t::R out ::R+ begin return x + v * t + (9.80665 * t ^ 2) / 2 end De ahí que se prefiera a veces usar funciones en formato expresión.¿ EJEMPLO.
Escribir una función expresión en pseudocódigo papercode que pueda ser usada para determinar el si un triángulo es equilátero. R/ esEquilátero(a::Num, b::Num, c::Num)::Num = a.EQ.b ∧ b.EQ.c EJEMPLO.
Escriba una función expresión en pseudocódigo papercode que le permita calcular el volumen de una circunferencia dado su radio. R/ volumenEsfera( radio::R+)::R+ = 4 / 3 * 3.14159265358 ^ 3 EJEMPLO.
Escriba una función en pseudocódigo papercode que permita hallar el dígito menos significativo de la parte entera de un número real positivo. Por ejemplo, dado el número 473.98, la función entrega 3. Si el número no tiene parte entera, la función entrega cero. R/ En este caso, una función expresión es suficiente. dígitoMenosSignifParteEntera( num::R+ ) ::N = ::Z(num) % 10 |
FUNCIONES CON EXPRESIONES BOOLEANAS
El primer ejemplo que presentamos de funciones en este sector del libro incluye expresiones con operadores lógicos, lo que significa que nuestras funciones pueden decir si algo es cierto, o falso. Esto es bastante poderoso en computación, y veremos más adelante en los capítulos de bifurcaciones condicionales no cíclicas y las bifurcaciones condicionales cíclicas, que las expresiones lógicas son la base para lograr controlar cómo el código se bifurca.
Es importante aprender a dominar la técnica del uso de expresiones lógicas, para facilitar más adelante la solución de muchos problemas, y la mejor forma de hacerlo, es a través de ejemplos.
Lo importante, antes de comenzar, es que, como ya se vio, el resultado de una expresión booleana es un valor cierto, o un valor falso, que sabemos se representa en una expresión como .t. o .f. (los dos posibles valores booleanos).
Es importante aprender a dominar la técnica del uso de expresiones lógicas, para facilitar más adelante la solución de muchos problemas, y la mejor forma de hacerlo, es a través de ejemplos.
Lo importante, antes de comenzar, es que, como ya se vio, el resultado de una expresión booleana es un valor cierto, o un valor falso, que sabemos se representa en una expresión como .t. o .f. (los dos posibles valores booleanos).
EJEMPLO: Círculo inscrito.
Un círculo puede ser inscrito en un cuadrado, si el diámetro del círculo es igual al lado del cuadrado. Dados el lado de un cuadrado y el radio de un círculo, determinar si el círculo puede se inscrito en el cuadrado. R/ Se requiere una función con dos parámetros, uno el lado del cuadrado, y el otro es el radio del círculo. fx circulo_puede_inscribirse in lado::Num, radio::Num out ::Bool begin lado .EQ. (2 * radio) end Pero tal vez para esta función, es mejor una función expresión: circulo_puede_inscribirse (lado::Num, radio::Num) :: Bool = lado .EQ. (2 * radio) Lo único que debe hacer la función es comprobar si el lado equivale a dos veces el radio. Al compararse con el operador igual (.EQ.), la expresión devuelve un valor booleano. Por ejemplo, si la llamada a la función es: q <- circulo_puede_inscribirse (8.2, 4.1) #q es verdadera p <- circulo_puede_inscribirse (8.7, 2.1) #p es falsa EJEMPLO: Isosceles
Escriba una función que determine si las medidas de un triángulo es isóceles (dos lados iguales y uno desigual), dada la longitud de sus tres lados a, b y c. Un triángulo equilátero no se considera isósceles, de modo que, si hay dos lados iguales, el otro deberá ser desigual. R/ Dado que se valida tres valores numéricos, podemos crear una función con tres parámetros. El nombre de la función deberá ser lo más claro posible en su propósito. Debido a que la función debe indicar si los tres números son iguales, el retorno de la función deberá ser tipo booleano. fx es_isóceles in a::Num, b::Num, c::Num out ::Bool begin x <- a = b ∧ c ≠ a y <- a = c ∧ b ≠ a z <- b = c ∧ a ≠ b iso <- x ∨ y ∨ z ret iso end Para mejor entendimiento de las posibilidades, se plantean las distintas opciones en términos de igualdades de dos lados, y desigualdad de un tercero. Por ejemplo, la expresión x <- a = b ∧ c ≠ a valida que dos lados sean iguales, y un tercer lado sea desigual al valor de los otros dos, y deja el resultado en una variable para uso posterior. Finalmente se verifica si se da alguna de las tres condiciones por medio de iso <- x ∨ y ∨ z EJEMPLO DE LLAMADO DE LA FUNCIÓN. Para llamar la función, simplemente la nombramos y ponemos entre paréntesis los 3 argumentos, que son los valores de los tres lados. La función devolverá un valor falso o verdadero según sea el caso: w <- es_isóceles(3 , 8 , 8 ) En este ejemplo, la expresión que se cumple como verdadera dentro de la función es z <- b == c ∧ a ≠ b Por lo tanto, z es verdadera (.t.). Las otras dos expresiones no se cumplen, por lo que x es falsa, así como también lo es la y. Finalmente, se evalúa la expresión iso <- x ∨ y ∨ z y dado que la variable z es verdadera y se trata de la función OR, el resultado es que la variable iso toma el valor de verdadero (.t.), que es el valor que finalmente devuelve la función en el ejemplo específico. [BIO]
EJEMPLO: Isoleusina. Un codón es un fragmento de RNA, que se compone de 3 nucleótidos que en conjunto representan un aminoácido concreto. Elabore una función que determine si un aminoácido dado como cadena de 3 caracteres, representa una isoleucina. El aminoácido Isoleucina es representado por cualquiera de las siguientes secuencias: ‘AUU’, ‘AUC’ , ’AUA’. R/ Dado que el aminoácido isoleucina, puede ser representado por cualquiera de las tres secuencias ‘AUU’, ‘AUC’ , ’AUA’. Usaremos un OR o disyunción para hacer la evaluación. Recordemos: una función OR es verdadera, si alguno de sus dos argumentos es verdadero. Podemos comprobar las igualdades, y aplicamos a esos resultados operadores OR con las tres igualdades. Note que el parámetro codón de la función es de tipo ::String (cadena de caracteres), y tenemos que tratar los valores que buscamos entre comillas. Una versión simplificada sin el uso de variables es: fx esIsoleucina in codon::String out ::Bool begin return codon == "AUU" ∨ codon == "AUC" ∨ codon == "AUA" end Un ejemplo de uso sería: c1 <- "AUG" c2 <- "AUU" x <- esIsoleucina(c1 ) #x será .f. x <- esIsoleucina(c2 ) #y será .t. [BIO]
EJEMPLO: Masa orgánica. Se sabe que en el medio de cultivo agar, un hongo puede generar un volumen máximo de masa orgánica MOH. Dados el radio y la altura de un plato petri, determine si es posible sembrarlos en dicho petri. R/ fx siembra_posible in MOH::Num, radioPetri::R+, altPetri::R+ out ::Bool begin const p::R <- 3.14159265 volumenPetri <- p * radioPetri ^ 2 * alturaPetri return volumenPetri ≤ MOH end EXPLICACIÓN. Dado que un plato petri es un cilindro con tapa, calculamos su volumen como el área de la base por la altura. En el ejemplo, este valor calculado lo almacenamos en una variable llamada volumenPetri, que deberá ser mayor o igual al volumen máximo del hongo MOH para poder contenerlo. La comparación devolverá un valor verdadero o falso, dependiendo de si el volumen del hongo cabe o no en el volumen del petri. Un ejemplo del uso de la función previa sería: x <- siembra_posible(168.6 , 5.3 , 2.1 ) #x será .t. En este caso, el radio del petri es 5.3, y su altura es 2.1. Esto nos da un volumen para el petri de 185.3119, y nos dicen que MOH es 168.6. En este caso, el volumen del petri es mayor que el volumen MOH por lo que la función al evaluar la expresión booleana, da como resultado un valor verdadero (.t.). y <- siembra_posible(168.6 , 4.4 , 1.9 ) #y será .f. En este caso, el volumen para el petri de 115.56, y nos dicen que MOH es 168.6. En este caso, el volumen del petri es menor que el volumen MOH por lo que la función da como resultado un valor falso (.f.). [BIO]
EJEMPLO: pH La concentración de iones de hidrógeno H+, puede ser expresada en la escala pH por medio de la fórmula pH = -log(H+). Para valores de pH entre 0 y 6, el pH se considera Ácido. Para valores de pH entre 8 y 14, el pH se considera Básico. Un valor de 7 se denomina pH neutro. Elabore una función que determine si una concentración H+ dada corresponde a un pH Ácido. Asuma para su pseudocódigo la existencia de una función log( ) que puede usar para el cálculo. R/ fx pHacid in concentración::R+ out ::Bool begin pH <- -log(concentración) return pH < 7 end Nuestro algoritmo es de dos pasos: calcular el pH, y comprobar si es ácido. Por ejemplo, una llamada a la función para que haga el cálculo es: x <- pHacid(1.0 x 10^-7) da como resultado que el pH sea 7, que es neutro. Dado que 7 no es menor que 7, el resultado es que el pH no es ácido, y por lo tanto la función pHacid da valor falso (.f.) y x quedará con ese valor. |
COOPERACIÓN FUNCIONAL
Volvamos al tema de cómo se pueden invocar las funciones. Esta vez lo haremos desde la óptica de cómo una función llama a otra para uso interno.
Toda función se construye con un único propósito: servir como ladrillo a ser usado en otras funciones.
Para llamar una función de tal modo que preste sus servicios a otra función, simplemente se pone su nombre y los argumentos entre paréntesis.
Los argumentos son los valores que serán asociados a los parámetros de la función que se va a llamar; de ese modo la función es llamada, y el valor resultante es usado dentro de la expresión que use la llamada.
Una expresión puede usar tantas llamadas a funciones como necesite, y dado que los argumentos en una llamada a una función son valores, estos argumentos pueden ser expresiones.
Toda función se construye con un único propósito: servir como ladrillo a ser usado en otras funciones.
Para llamar una función de tal modo que preste sus servicios a otra función, simplemente se pone su nombre y los argumentos entre paréntesis.
Los argumentos son los valores que serán asociados a los parámetros de la función que se va a llamar; de ese modo la función es llamada, y el valor resultante es usado dentro de la expresión que use la llamada.
Una expresión puede usar tantas llamadas a funciones como necesite, y dado que los argumentos en una llamada a una función son valores, estos argumentos pueden ser expresiones.
EJEMPLO: El ciclista del desierto
Un ciclista practica larga distancia en un desierto plano que tiene coordenadas oficiales como plano cartesiano. Las unidades del plano desértico están en kilómetros. Cada mañana le pide a su familia que le den 4 números positivos al azar: dos números los asigna a x1 y y1, y los otros dos números los asigna a x2,y2. Los cuatro números se los entrega a la app de plan de ruta para que guíe al ciclista. La app calcula dos rutas con el GPS del celular: una desde el origen (0,0) al primer punto (x1,y1), y otra ruta desde el punto (x1, y1) hasta el punto (x2,y2). El ciclista usa la app cada mañana, y desea mejorarla para que también le indique con anticipación la distancia que recorrerá ida y vuelta, y el tiempo que le tomará hacer el circuito a una velocidad constante dada en kilómetros por hora. Escriba un pseudocódigo papercode la función que se implementará en la app, que permite calcular el tiempo en horas y la distancia recorrida en kilómetros, para el total del recorrido de ida y vuelta entre los tres puntos. El ciclista ingresa la velocidad y los puntos (x1,y1), (x2,y2). R/ Se sabe por física, que velocidad es distancia dividida por el tiempo, por lo que el tiempo se despeja como distancia dividido la velocidad. Primeo podemos construir una función distancia para calcular la distancia entre dos puntos: fx distancia in (x1::R+, y1::R+, x2::R+ , y2::R+) out ::R+ begin ((x2 - x1)^2 + (y2 - y1)^2)) ^ 0.5 end luego construimos una función trayectorydata( ) que determina el tiempo que toma recorrer las distancias requeridas, primero desde el punto (0,0) hasta el punto(x1,y1) y luego desde el punto (x1,y1) hasta el punto (x2,y2). Conocidas las distancias, simplemente dividiremos por la velocidad (dato que se conoce) para obtener el tiempo. La función trayectorydata( ) usa la función distancia( ) para conocer las dos distancias recorridas entre los tres puntos. fx trayectorydata in x1::R+, y1::R+, x2::R+, y2::R+ ,vel::R+ out ::R+, R+ begin primeradist <- distancia(0 , 0 , x1 , y1) #1 segundadist <- distancia(x1 , y1 , x2 , y2) #2 distanciatotal <- 2 * ( primeradist + segundadist) #3 tiempo <- distanciatotal / velocidad #4 return distanciatotal, tiempo #5 endfx EXPLICACIÓN. Primero se calculan las dos distancias, una desde el punto (0,0) al punto(x1,y1), y otra desde el punto (x1,y1) hasta (x2,y2), y que referenciamos con las variables primeradistancia y segundadistancia respectivamente. (#1, #2) La distancia total se calcula como el doble de la distancia entre los puntos, dado que el recorrido es ida y vuelta. (#3) El tiempo se calcula dividiendo la velocidad, que es un parámetro de la función, sobre la distancia total que se recorrería. (#4) Finalmente se devuelven los dos valores requeridos: la distancia y el tiempo. (#5) Supongamos que queremos conocer la distancia y el tiempo para ir primero al punto (32,14) y desde allí, ir al punto (27,54), a una velocidad de 70 km/h. La llamada a la función quedaría: d,t <- trayectorydata( 32, 14, 27, 54, 70) La función devuelve dos valores.Si se hace una prueba en papel de la llamada, vemos que los valores obtenidos son d = 150.48 y t= 2.15. Esto es, para la información de entrada, obtendremos una distancia de 150.48 kilómetros recorridos en 2.15 horas. EJEMPLO: Balón en caja
Escriba una función de bloque en pseudocódigo papercode que, dado el volumen de un balón, determine el lado de la caja y el volumen desperdiciado(no ocupado por el balón) dentro de cada caja en la que el balón cabe exactamente tocando las paredes. El volumen de una esfera es 4/3 * p * radio3, o lo que es lo mismo, 4/3 * p * (diámetro/2)3. El volumen de un cubo es lado^3. Determine con seguimiento manual, el desperdicio para esferas de 5 cm^3, 10 cm^3, 20 cm^3 y 30 cm^3. Para los cálculos de volúmenes, use funciones independientes de apoyo. R/ Meter la esfera en la caja de tal modo que quepa exactamente, significa que el diámetro de la esfera es igual al lado del cubo. Debemos calcular entonces el diámetro con solo despejar desde el volumen. volumen = 4/3 * π * (diámetro/2)3 3/4 * volumen / π = (diámetro/2)3 (3/4 * volumen /π )1/3 = diámetro/2 diámetro = 2*(3/4 * volumen / π )1/3 Conocido el diámetro, ya tenemos el lado del cubo, y podemos hallar el volumen del cubo. Y conocido el volumen del cubo, solo le restamos el volumen de la esfera para conocer el desperdicio. #función bloque para el diámetro de una esfera, dado el volumen fx diamesfera in volesfera::R+ out ::R+ begin p::R <- 3.14159 return 2*(3/4 * volesfera / π )^(1/3) end #función bloque para el volumen de un cubo fx volCubo in lado::R+ out ::R+ begin lado * lado * lado endfx finalmente, podremos crear nuestra función bloque de cálculo de desperdicio. #función bloque para el desperdicio fx desperdicio in volEsfera::R+ out ::R+ begin diam <- diamesfera(volEsfera) volCaja <- volCubo(diam) return volCaja - volEsfera endfx Si analizamos llamadas diversas para esferas de 5 cm^3, 10 cm^3, 20 cm^3 y 30 cm^3, tenemos: desperdicio(5) es 4.54929659642538 desperdicio(10) es 9.09859319285076 desperdicio(20) es 18.19718638570152 desperdicio(30) es 27.29577957855228 |
VALIDACIÓN DE ARGUMENTOS DE ENTRADA
Cuando trabajamos con funciones, asumimos que los parámetros reciben argumentos correctos.
Sin embargo, siempre deberemos protegernos contra problemas de datos.
Para garantizar que hay un mínimo de consistencia, crearemos para nuestros pseudocódigos papercode el concepto de validaciones en el ingreso de los parámetros. Una validación se basa en una condición que se supone debe ser verdadera.
Si no lo es, la función no correrá y devolverá un error, por lo que se asume que una validación es un mecanismo de documentación y consistencia en las llamadas.
Se usará la palabra valid, seguida de una condición, y un código opcional de excepción usando la palabra [codex]. El código de excepción se puede asociar a lo que los lenguajes de programación manejan como Tipo de Excepción: es una marca, numérica o string en pseudocódigo, que sirve para identificar el tipo de validación que no se cumple. Un ejemplo de codex podría ser una string que indica un error, o el código de dicho error.
Si se requiere múltiples condiciones, usamos un conector and, o el caracter coma ( , ) que hará las veces de and. Si se necesita que se de alguna condición entre varias, usamos conectores or. También podremos usar en pseudcódigo varias sentencias valid separadas, cada una con su codex.
este conjunto de validaciones funcionará en nuestros pseudocódigos al momento de una llamada, y emitirá un estado especial de error que deberá ser controlado por al momento de la llamada.
Los lenguajes de programación normalmente no manejan esta estrategia directa de validaciones, pero cuentan con generación de errores y manejo de error que cumple con el objetivo, o cuentan con instrucciones assert para pruebas que se pueden usar para el mismo objetivo.
Sin embargo, siempre deberemos protegernos contra problemas de datos.
Para garantizar que hay un mínimo de consistencia, crearemos para nuestros pseudocódigos papercode el concepto de validaciones en el ingreso de los parámetros. Una validación se basa en una condición que se supone debe ser verdadera.
Si no lo es, la función no correrá y devolverá un error, por lo que se asume que una validación es un mecanismo de documentación y consistencia en las llamadas.
Se usará la palabra valid, seguida de una condición, y un código opcional de excepción usando la palabra [codex]. El código de excepción se puede asociar a lo que los lenguajes de programación manejan como Tipo de Excepción: es una marca, numérica o string en pseudocódigo, que sirve para identificar el tipo de validación que no se cumple. Un ejemplo de codex podría ser una string que indica un error, o el código de dicho error.
Si se requiere múltiples condiciones, usamos un conector and, o el caracter coma ( , ) que hará las veces de and. Si se necesita que se de alguna condición entre varias, usamos conectores or. También podremos usar en pseudcódigo varias sentencias valid separadas, cada una con su codex.
este conjunto de validaciones funcionará en nuestros pseudocódigos al momento de una llamada, y emitirá un estado especial de error que deberá ser controlado por al momento de la llamada.
Los lenguajes de programación normalmente no manejan esta estrategia directa de validaciones, pero cuentan con generación de errores y manejo de error que cumple con el objetivo, o cuentan con instrucciones assert para pruebas que se pueden usar para el mismo objetivo.
EJEMPLO: Raiz cuadrada de una suma
Escriba una función en pseudocódigo papercode que halle la raíz cuadrada de la suma de dos números. La suma debe ser siempre un número real positivo, o cero. R/ fx raizDeSuma in a::R+, b::R+ valid: a + b > 0, codex: "número cero o negativo no permitido" out ::R+ begin return ( a + b ) ^ 0.5 end En el caso que la suma de a y b sea negativa, la función emitirá un mensaje de error a la función que la llamó, y cancelará la llamada. |
FUNCIONES ANÓNIMAS O LAMBDA
Una función no necesita tener nombre. A una función sin nombre se le denomina función anónima. Las funciones anónimas son funciones que pueden pasar como argumentos a otras funciones o que devuelven como valor de retorno funciones, o ambas. Debido a que no requieren nombre, se le conocen también como funciones lambda. Las funciones anónimas se originaron con el trabajo de Alonzo Church en su invención del cálculo lambda en 1936, antes de los computadores electrónicos.
En el cálculo lambda, todas las funciones son anónimas (Fernández, 2009).
En pseudocódigo, se puede declarar una función lambda simplemente como
λ(parámetros,…) => expresión
Note que no se usa la estructura acostumbrada para una función, y una función lambda solo permite una expresión.
En nuestros pseudocodigos, se pone la letra griega lambda λ, luego los parámetros entre paréntesis, y, a continuación, una flecha a derecha escrita como =>. Y finalmente una expresión.
La letra griega λ es opcional, pero en pseudocódigo es útil para indicar que se trata de una función lambda que no tiene nombre, y que, por lo tanto, es anómima.
Las funciones lambdas se dan “al vuelo” dentro de nuestro código, y pueden ser almacenadas donde se desee: una variable, un arreglo, estructura, tupla, parámetro a otra función, valor de retorno, etc.
Una función lambda en pseudocódigo tiene tipo ::function como cualquier otra función que no sea anónima, y cuando se encuentra almacenada puede ser invocada a través del contenedor que la referencia.
f <- λ(x) => x ˆ 2 + x ˆ 3 – 2x + 7
g <- λ(x,y) => 3( y * log(x) + x * log(y) ) ^ 0.5
w <- [f,g]
Las variables f y g contienen funciones que fueron almacenadas en el arreglo w en las posiciones 1 y 2. Por lo tanto, podríamos invocar alguna función a través del arreglo w:
c <- w[1](0.1) + w[2](0.1,0.2)
Una función lambda no tiene que ser llevada a una variable antes de ser puesta a disposición de otras estructuras de datos, por lo que el código presentado puede simplificarse como
w <- [
λ(x) => x ˆ 2 + x ˆ 3 – 2x + 7,
λ(x,y) => 3( y * log(x) + x * log(y) ) ^ 0.5
]
La anterior expresión surte el mismo efecto.
Las funciones lambda simplifican muchas veces la escritura de código. Sin embargo, no se debe abuzar de ellas, ya que pueden obscurecer las intenciones del código.
En el cálculo lambda, todas las funciones son anónimas (Fernández, 2009).
En pseudocódigo, se puede declarar una función lambda simplemente como
λ(parámetros,…) => expresión
Note que no se usa la estructura acostumbrada para una función, y una función lambda solo permite una expresión.
En nuestros pseudocodigos, se pone la letra griega lambda λ, luego los parámetros entre paréntesis, y, a continuación, una flecha a derecha escrita como =>. Y finalmente una expresión.
La letra griega λ es opcional, pero en pseudocódigo es útil para indicar que se trata de una función lambda que no tiene nombre, y que, por lo tanto, es anómima.
Las funciones lambdas se dan “al vuelo” dentro de nuestro código, y pueden ser almacenadas donde se desee: una variable, un arreglo, estructura, tupla, parámetro a otra función, valor de retorno, etc.
Una función lambda en pseudocódigo tiene tipo ::function como cualquier otra función que no sea anónima, y cuando se encuentra almacenada puede ser invocada a través del contenedor que la referencia.
f <- λ(x) => x ˆ 2 + x ˆ 3 – 2x + 7
g <- λ(x,y) => 3( y * log(x) + x * log(y) ) ^ 0.5
w <- [f,g]
Las variables f y g contienen funciones que fueron almacenadas en el arreglo w en las posiciones 1 y 2. Por lo tanto, podríamos invocar alguna función a través del arreglo w:
c <- w[1](0.1) + w[2](0.1,0.2)
Una función lambda no tiene que ser llevada a una variable antes de ser puesta a disposición de otras estructuras de datos, por lo que el código presentado puede simplificarse como
w <- [
λ(x) => x ˆ 2 + x ˆ 3 – 2x + 7,
λ(x,y) => 3( y * log(x) + x * log(y) ) ^ 0.5
]
La anterior expresión surte el mismo efecto.
Las funciones lambda simplifican muchas veces la escritura de código. Sin embargo, no se debe abuzar de ellas, ya que pueden obscurecer las intenciones del código.
CRITERIOS ÚTILES SOBRE FUNCIONES
La forma en que se escriban funciones de ahora en adelante va a definir tu capacidad para abstraer problemas, y para brindar soluciones adecuadas. A continuación, se resumen algunos criterios útiles.
- Todo parámetro de ingreso a la función deberá tener un tipo de dato en pseudocódigo papaercode.
- Toda función deberá devolver un tipo de dato, y si no es así, su diseño es sospechoso, ya que las funciones están pensadas para devolver valores. Si definitivamente no devuelve valor, no se considera función sino procedimiento. No hay nada malo en un procedimiento, pero si se usa, es porque tendrá algún efecto colateral distinto a devolver un valor.
- Haga que sus funciones sean legibles por otras personas. Eso muchas veces se logra fraccionando expresiones complejas en conceptos más claros a través del uso de variables. También es importante que las funciones cumplan el principio de responsabilidad única.
- Use nombres de variables claros que permitan establecer su propósito o sentido con solo leerlas.
- Une nombres de funciones que describan el propósito de la función. Si darle un nombre a la función se ve difícil, es porque su función está mal pensada.
- Toda función debe tener un único propósito. No permita que una función sea multipropósito.
- Sospeche de funciones con un único parámetro de tipo booleano, pues es probable que se trate de una función multipropósito.
- No permita que una función que está resolviendo un problema interactúe con el usuario. Las funciones deben hacer su trabajo sin interrupción.
- Si su función es grande, revise la posibilidad de estructurarla como un conjunto de funciones pequeñas que puede llamar. De ese modo su función será más manejable y modificable.
- Si su función tiene muchos parámetros, revise el propósito de su función, ya que muchos parámetros son a veces síntoma de funciones multipropósito. Aludiendo el principio de responsabilidad única de las funciones, no es bueno tener funciones multipropósito.
- Si su función tiene muchos parámetros y definitivamente no puede fragmentada en más funciones, use como parámetros tipos compuestos, como estructuras, tuplas o arreglos.
- Su función deberá estar destinada a dar como resultado un único tipo de dato. Si necesita devolver más de un dato, use tuplas, estructuras, o arreglos.
- Cuando necesite ingresar o presenta datos, use funciones dedicadas al propósito de interactuar con el usuario como veremos en el capítulo siguiente.
TALLER SOBRE BLOQUES FUNCIONALES
Taller Bloques Funcionales | |
File Size: | 326 kb |
File Type: |