Estructuras de Control

Operadores

Los operadores se pueden definir como símbolos que permiten realizar diferentes operaciones entre valores o datos de un programa. Con ellos se pueden conectar dos más expresiones, cuyo valor final para la expresión compuesta depende del valor de las expresiones que fueron conectadas.

Existen diferentes tipos de operadores, entre ellos: aritméticos, asignación, relacionales, lógicos, etc. De ellos se hace especial mención de los operadores lógicos y relacionales, ya que son usados para la creación de test o comprobaciones con los cuales es posible controlar el flujo de un programa junto con las denominadas estructuras de control.

Los operadores lógicos y relacionales permiten evaluar y crear expresiones booleanas. Las expresiones de este tipo son aquellas que pueden tener la condición de true o false.

Operadores relacionales

Son operadores que nos permiten determinar un valor de verdad para una situación concreta, en el cual se necesita comparar determinados valores. Las operaciones de comparación estándar, en Julia, se definen para todos los tipos numéricos primitivos y estos son:

Operador de igualdad:

El operador de igualdad es denotado por ==

(5 == 4, 5 == 5.0, 5 == 5)
(false, true, true)

Operador de igualdad estricto:

El operador de igualdad estricta se denotado por === o bien,

(5 === 5.0, 5.0  5.0)
(false, true)

El símbolo ≡ se introduce escribiendo \equiv+<TAB>.

Operador de desigualdad o no-igualdad:

El operador de desigualdad o no-igualdad se denota por != o .

(1 != 1, 1  3)
(false, true)

El símbolo ≠ se introduce escribiendo \ne+<TAB>.

Operador de desigualdad estricto:

El operador de desiagualdad o no-igualdad estricta es denotado por !== o .

(5 !== 5.0, 5  5)
(true, false)

El símbolo ≢ se introduce escribiendo \nequiv+<TAB>.

Operador de comparación mayor y menor que:

Tenemos múltiples operadores de comparación de órden, o comúnmente conocidos por desigualdades (en dicho caso, se llama a las comparaciones anteriores no-igualdades). Estas son:

(-1 < 1, -1 <= -2, -1  -1, 1.0 > 3, 1 >= 1.0, -1.5  1.0)
(true, false, true, false, true, false)

El símbolo ≥ se introduce escribiendo \ge+<TAB> y el símbolo ≤ se introduce mediante \le+<TAB>

Operadores lógicos

Son operadores que trabajan sobre valores del tipo booleano (true, false) y componen expresiones booleanas más complejas, en su mayoría a partir de varias expresiones que involucran operadores relacionales.

Los lenguajes de programación incorporan 3 operadores: AND y OR, los cuales son operadores binarios; NOT, que es un operador unario.

Operador AND

Este operador retorna el valor booleano true solamente si los dos operandos son verdaderos (true). En Julia el símbolo usado para el operador es &&.

true && true
true
true && false
false
false && true
false
false && false
false

Operador OR

Este operador retorna el valor booleano false solamente si los dos operandos son falsos (false). En Julia el símbolo usado para el operador es ||.

false || false
false
false || true
true
true || false
true
true || true
true

Operador de cortocircuito

Los operadores lógicos en Julia se evaluan mediante “Short-Circuit Evaluation”, esto significa que se siempre se trata de hacer el menor número de evaluaciones.

  • Para la expresión a && b, la subexpresión b se evalúa sólo si a es true, ya que la expresión será false si a lo es y ello independiente del valor de b.

  • Para la expresión a || b, la subexpresión b se evalúa sólo si a es false, ya que la expresión será true si a lo es y ello independiente del valor de `b.

NOTA: Los operadores && y || no deben ser confundidos con los operadores & y |, estos últimos son operadores denominados bitwise (bit a bit).

####### Operador NOT Este operador retorna el valor booleano contrario sobre el cual opera. En Julia el símbolo usado para el operador es !.

!false
true
!true
false

Precedencia de los operadores

Los operadores lógicos pueden escribirse de la siguiente forma:

 1-5 > 0 && 2+2 == 4 || 10-4 == 2*3 && true
true

La expresión anterior es ambigua porque no se conoce cómo se ejecutan los operadores. Para aclarar la forma en que Julia ejecuta la sentencia, se debe tomar en cuenta la precedencia de los operadores.

En primer lugar los operadores lógicos tienen mayor precedencia, por lo que la expresión de ejemplo se reduce a:

false && true ||  true && true
true

Finalmente, la última expresión se evalúa de izquierda a derecha. Aunque es útil saber la manera en que Julia ejecuta los operadores lógicos, a menudo se prefiere utilizar los paréntesis para eliminar las ambiguedades, permite que exista más legibilidad en el código.

 ((1-5 > 0 && 2+2 == 4)|| 10-4 == 2*3) && true
true
 1-5 > 0 && (2+2 == 4 || 10-4 == 2*3) && true
false

Otros operadores

Los lenguajes de programación implementan otros tipos de operadores, los mencionados a continuación son importantes para el uso de estructuras de control.

Operadores de pertenencia

Son usados para comprobar si un elemento se encuentra en un determinado conjunto de datos, los cuales pueden ser del tipo: array, matrices, diccionarios o conjuntos.

1 in [0,2,3,5]
false
1  [0,1,2,3,5]
true
6.0  [0,1,2,3,5]
true

Los símbolos anteriores se introducen de la siguiente manera: ∈ mediante \in+<TAB>,∉ mediante \notin+<TAB>

NOTA: Una fuente de consulta adicional sobre los diferentes tipos operadores y funciones especiales en Julia, puede encontrarlo en el manual de Julia.

Estructuras de Control

En general un programa está definido por un algoritmo, que constituye una guía que lista los pasos a seguir para la resolución de un problema. Es por ello que todo lenguaje de programación de alto nivel posee definida ciertas estructuras que proveen una forma útil para organizar el algoritmo y permiten manipular el flujo de un programa, esto es, crear diferentes caminos para la ejecución de tareas o sentencias que definen el programa.

Las estructuras de control, en lenguajes de programación, a menudo están referidos a sentencias que permiten: la elección de ejecución de código mediante condiciones o decisiones y repetición en la ejecución de bloques de código mediante bucles. En la construcción de tales estructuras se utilizan los operadores anteriormente mencionados.

Estructura secuencial

Por defecto las instrucciones de un programa se ejecutan de manera secuencial, es decir, en el orden en el cual están descritas; esta forma de ejecución se conoce como estructura secuencial.

begin
	x = 1
	y = 2
	x + y;
end
3

Los keywords begin y end conforman delimitadores para un bloque de código, considerandolo como una expresión. Incluso, el bloque de código puede ser asignado a una variable. Su principal uso consiste en evaluar varias expresiones encadenadas, retornando el valor de la última subexpresión.

Estructura de selección

Esta estructura permite decidir qué bloque debe ejecutarse mediante la evaluación de una condición o expresión booleana. Julia implementa diferentes formatos:

  • if

  • if-elseif

  • Operador ternario

Instrucción if

La instrucción if permite comprobar el cumplimiento de una condición, en dicho caso se ejecuta un determinado bloque de código, si es lo contrario es posible ejecutar un grupo diferente de líneas de código.

La función testtriangle respresenta un test de comprobación para saber si dada una terna de números, estos pueden representar las longitudes de los lados de un triángulo.

function testtriangle(a,b,c)
	if a+b>c && a+c>b && b+c>a ##Condición
		return "Las longitudes dadas representan los lados de un triángulo"
	else ##Bloque de código alternativo en caso de no cumplirse la condicíon
		return "Las longitudes dadas no representan los lados de un triángulo"
	end
end
testtriangle (generic function with 1 method)
testtriangle(1,1,5)
"Las longitudes dadas no representan los lados de un triángulo"

En el dessarrollo de la función se implementa la estructura de control if, donde si en efecto se cumple el criterio que en un triángulo la longitud de cada lado debe ser menor que la suma de los otros dos lados, se imprime la oración “Las longitudes dadas representan los lados de un triángulo”, en caso contrario se imprime “Las longitudes dadas no representan los lados de un triángulo”.

El bloque de código alternativo, else, es opcional, por ello puede prescindirse de dicho bloque si en el problema no se considera necesario.

function testtriangle2(a,b,c)
	if a+b>c && a+c>b && b+c>a
		return "Las longitudes dadas representan los lados de un triángulo"
	end
end
testtriangle2 (generic function with 1 method)
testtriangle2(5,5,5)
"Las longitudes dadas representan los lados de un triángulo"

Instrucción if-elseif

Esta instrucción permite implementar instrucciones if encadenadas, sucediendo lo anterior en caso que se necesite realizar más de una comprobación.

function whichtriangle(a,b,c)
	if a==b && b==c
		return "El triángulo es rectángulo"
	elseif a==b || b==c || a==c
		return "El triángulo es isóceles"
	else
		return "El triángulo es escaleno"
	end
end
whichtriangle (generic function with 1 method)
whichtriangle(5,5,5)
"El triángulo es rectángulo"
whichtriangle(4,5,5)
"El triángulo es isóceles"

Operador ternario

En otros lenguajes este operador también es llamado operador elvis. Permite tener una versión simplificada del operador if, el cual puede ser escrito en una sola línea de código.

Se suele utilizar cuando se requiere una elección condicional entre valores de expresión única, en contraposición a la ejecución condicional de bloques de código más largos.

mayor = x>y ? x : y ##las variables "x" y "y" fueron definidas previamente.
2

Al igual que las instrucción if, el operador ternario puede encadenarse.

begin
	testcomparación(x,y) = x>y ? "x es mayor a y" :
						   x<y ? "y es mayor a x" : "x es igual a y"
	testcomparación(x,y)
end
"y es mayor a x"

NOTAS:

  • Las estructuras de control if no introducen un nuevo ámbito local, significa que se puede declarar una nueva variable en la estructura y utilizarla fuera de este. Sin embargo, debe usarse con cuidado ya que puede introducir errores de sitaxis.

  • Devuelve el valor de la última expresión ejecutada en la estructura.

  • Los únicos valores aceptados en la condicional son los del tipo booleano. Significa que, a diferecia de otros lenguajes, no se permiten el uso de 0 o 1 en la condición.

Instrucción switch

Julia a diferencia de otros lenguajes de programación no implementa la instrucción Switch. Esta instrucción permite cambiar el flujo de un programa dependiendo del valor de una variable o expresión, permitiendo tomar decisiones cuando existe más de dos opciones.

Julia ofrece un paquetes adicional para implementar esta instrucción, el paquete Match.jl. Este paquete, así como los que usaremos el resto del curso, estarán ya especificados en los archivos Project.toml y Manifest.toml que controlan las dependencias de un proyecto.

De esta manera, para instalar paquetes, podemos ejecutar Pkg.instantiage() para actualizar nuestros paquetes hacia lo que Project.toml y Manifest.toml especifican.

using Pkg; Pkg.activate("."); Pkg.instantiate(); using Match
 Activating environment at `~/work/Intro-Julia-2021/Intro-Julia-2021/Project.toml`
function switchselect(x)
	@match x begin
		1 	 => "x tiene el valor de 1"
		2 	 => "x tiene el valor de 2"
		3||4 => "tres o cuatro"
		3:10  => "x es el rango 3:10"
		_    => "Caso default"
	end
end
switchselect (generic function with 1 method)
switchselect(3)
"tres o cuatro"

Las posibilidades con el paquete mencionados son varias, este guía la macro bajo la política de primera coinicidencia y se pueden establecer comparaciones entre distintos tipos de datos, además, coincidencia profunda con matrices. Puede encontrar mayor información en la documentación del paquete Match.jl

En determinadas ocasiones esta instrucción suele ser más eficiente que el encadenamiento de instrucciones if. Lo anterior debido que, cuando se tienen múltiples casos los if encadenados son evaluados hasta que se encuentra la condición que se cumple y en el peor de los casos se pueden evaluar todas las condiciones sin encontrar una condición que se cumple, ello hace que se tome un tiempo considerable de cálculo. Mientras que en la instrucción switch, la ejecución de un bloque de código está atada al valor de una expresión, por ello la ejecución o no del bloque es más directa que las los if encadenados.

Estructura de repetición

Esta estructura permite repetir un código las ocasiones que fueren necesarios. Julia implementa dos instrucciones para la evaluación repetida de expresiones: el bucle while y el bucle for.

Instrucción while

Con esta instrucción se especifica que una serie de tareas o líneas de código se ejecutan de forma repetida mientras una condición sea verdadera.

begin
	i = 0
	e = 0
	while i < 20 ##Condición
		t = 1/factorial(i) ##Cuerpo de la instrucción while
		e += t
		i+= 1
	end
end

La rutina anterior calcula el valor aproximado del número e, evaluando una serie hasta el vigésimo término. Se puede observar que las sentencias: e += 1/factorial(i) y i+= 1 se repiten, hasta que la condición (i < 20) deja de cumplirse.

El símbolo += es un operador de actualización, la línea de código i+=1 es equivalente a i=i+1. Existen diversos operadores de asignación similares, los cuales pueden ser consultados en la sección Updating operators del manual de Julia.

Cuando se utiliza el bucle while se debe tener el cuidado de asegurar que, en algún punto después de un número de repeticiones, la condición debe ser falso; de no hacerlo se tendrá un número infinito de repeticiones y por tanto el conocido bucle infinito, lo que constituye un error de programación.

En Julia la instrucción while, a diferencia de la instrucción if, introduce un nuevo scope o ámbito local; ello significa que cualquier variable al cual se asigna un valor dentro del while, este vive solo dentro del bucle mientras no se haya declarado antes de la instrucción while.

Para ilustrar este hecho puede observar las variables e y t del ejemplo anterior, como la variable e fue declarado antes de while este pertenece al ámbito global y posee un valor luego que el bucle se ejecuta.

e
2.7182818284590455

t, a diferencia de e, pertenece al ámbito local al while porque fue declarado dentro de este. Por ello al querer utilizar la variable en el ámbito local se genera un error.

## t # <- Dará error al ser evaluado, dado que t no está definido en el ámbito global.

Instrucción for

La instrucción for es similar al while, permite tener bucles o repetición de código controlado. Sin embargo, tiene algunas diferencias fundamentales. El ciclo for se constituye como sigue:

begin
	e₁ = 0
	for i  0:20 ##Encabezado
		t = 1/factorial(i) ##Cuerpo de la instrucción for
		e₁ += t
		i+= 1
	end
end
e₁
2.7182818284590455
##t # Causará error por la misma razón del ejemplo anterior. 

El ciclo for introduce un nuevo ámbito local como la hace el ciclo while, puede observar el comportamiento de las variables e₁ y t.

El encabezado del ciclo for está constituido por una variable (i en el ejemplo) y un iterable (el objeto 0:20) que constituye un secuencia de números desde 0 hasta 20 y cuyo incremento es de uno en uno. Además se define la relación de pertenencia entre la variable y el iterable.

El iterable contiene los valores que toma la variable (i) en cada iteración, por ello no necesariamente el ciclo for itera sobre valores numéricos. Además, si los valores son numéricos no necesariamente estos deberán tener un orden específico.

Observe que la cantidad de elementos que contiene el iterable constituye el número de iteraciones en el ciclo for.

Sentencias break y continue

break y continue son keywords que permiten manipular las iteraciones en las estructuras de control de repetición.

El keyword break para la ejecución del bucle.

begin
	z = 0
	for i in 1:10
		if i >= 6
			z = i
			break
		end
		z = i
	end
	z
end
6

Aunque el ciclo for está definido para iterar sobre los números del 1-10, se observa que solo itera sobre los números 1-6, debido a que en su interior se encuentra la sentencia if que ejecuta break cuando la variable i alcanza o supera el valor de 6.

El keyword continue detiene la iteración actual y hace que el ciclo continúe en la iteración siguiente.

begin
	w = ""
	for i in 1:10
		if i%2 == 0
			continue
		end
		w *= "$i"
	end
	w
end
"13579"

Como se puede observar el valor de la cadena final solo contiene los elementos que son impares, debido a que w *= \"$i\" no se ejecuta para i par, lo anterior porque se observa el efecto de ejecutar continue.

NOTA: Para conocer más detalles acerca de las estructura de control en Julia puede consultar la sección Control Flow del maual de Julia.