Clase 2: Control de flujo y estructuras de datos
Objetivos
- Aprender a crear y manipular arrays y slices en Go, realizando operaciones como inserción, eliminación y actualización de elementos.
- Comprender y aplicar las operaciones básicas en maps, como la obtención de un valor dado su clave y la verificación de la existencia de una clave en el map.
- Comprender los conceptos fundamentales del control de flujo en Go, incluyendo las instrucciones condicionales (if-else, switch), los bucles (for) y las instrucciones de salto (break, continue).
Contenido de la clase:
1. Arrays y slices: creación, manipulación y operaciones básicas.
Los arrays y los slices son estructuras de datos fundamentales en la programación.
- Arrays:
- Son una secuencia contigua de elementos del mismo tipo con tamaño fijo.
- Se definen utilizando una longitud específica durante la declaración.
- Los arrays tienen un tamaño estático que no puede modificarse una vez creado.
- La copia de un array implica copiar todos sus elementos.
- La asignación entre arrays implica copiar todos sus elementos.
- El acceso a los elementos se realiza mediante índices.
- La longitud de un array se obtiene utilizando la función
len().
package main
import "fmt"
func main() {
// Declarando array - Se inicializa con 0 todos los elementos por defecto
var numbers [3]int
// Reasignando valores
numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
fmt.Println(numbers) // [1 2 3]
var students [3]string = [3]string{"Carlos", "Juan", "Jose"}
fmt.Println(students) // [Carlos Juan José]
// Asignación corta y usando indices
cart := [3]string{"Eggs", 2: "Milk"}
fmt.Println(cart) // [Eggs "String Vacio" Milk]
// Sin una longitud establecida
courses := [...]string{"Go", "Nest"}
fmt.Println("Length: ", len(courses)) // 2
fmt.Printf("%T \n", courses) // [2]string
newCourses := courses // Diferentes espacios de memoria
newCourses[0] = "JS"
fmt.Println(courses, newCourses) // [Go Nest] [JS Nest]
}El tamaño del array es una parte del tipo. Por eso [5]int y [25]int son tipos distintos. No te preocupes por esta restricción ya que existen los slices que no tienen esa restricción.
- Slices:
- Son una vista flexible y dinámica de una sección de un array.
- Se crean a partir de un array existente o utilizando la función
make().
- Los slices no tienen un tamaño fijo y pueden crecer o reducirse dinámicamente.
- Comparten memoria con el array subyacente, por lo que las modificaciones en un slice afectan al array original y viceversa.
- La copia de un slice solo copia la referencia al array subyacente, no los elementos en sí.
- La asignación entre slices simplemente copia la referencia al array subyacente.
- El acceso a los elementos se realiza mediante índices.
- La longitud de un slice se obtiene utilizando la función
len(), y la capacidad se obtiene utilizando la funcióncap().
- Los slices admiten operaciones adicionales, como agregar elementos con
append(), recortar un slice conslicingy copiar slices concopy().
- Para eliminar un índice particular, se debe formar un slice a partir de otros slices.
package main
import "fmt"
func main() {
// Declaración de un slice
var mySlice []int
fmt.Println(mySlice)
// Crear un slice
// Slice a partir de un array
var names = [3]string{"Julio", "Claudio", "Oscar"}
newNames := names[:]
newNames[0] = "Osman" // Modifica al Array original y se modifica el Slice actual
fmt.Println(names, newNames, len(names), cap(names))
// Slice utilizando la función make(tipo_dato, len, cap(optional))
courses := make([]int, 3, 5)
fmt.Println(courses, len(courses), cap(courses))
// Añadir un elemento a un slice
// La función append es una función variádica, es decir que se le pueden enviar N argumentos al mismo tiempo.
var list []int = []int{1, 2, 3, 4, 5}
fmt.Println(list) // [1, 2, 3, 4, 5]
// list = append(list, 6, 7, 8, 9)
list2 := []int{6, 7, 8, 9}
// Agregando elementos de un Slice a otro con el operador elipsis
list = append(list, list2...) // [6, 7, 8, 9]
fmt.Println(list)
// Crear un slice de dos dimensiones
slice2D := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
// Agregar nuevo elemento
newSlice := []int{10, 11}
slice2D = append(slice2D, newSlice)
// Acceder a los elementos del slice de dos dimensiones
fmt.Println(slice2D[0][0]) // 1
fmt.Println(slice2D[1][1]) // 5
fmt.Println(slice2D[2][2]) // 9
// Modificar un elemento del slice de dos dimensiones
slice2D[1][2] = 99
fmt.Println(slice2D)
}En resumen, los arrays son estructuras estáticas con tamaño fijo, mientras que los slices proporcionan una vista más flexible y dinámica de una sección de un array. Los slices son más utilizados en Go debido a su capacidad de crecimiento y su facilidad para manipular y operar con ellos.
2. Maps: uso y manipulación de datos clave-valor.
El tipo de dato map se utiliza para trabajar con datos clave-valor. Un map es una estructura de datos que almacena pares de valores, donde cada valor está asociado a una clave única. Esto permite acceder rápidamente a los valores utilizando las claves correspondientes.
- Características
- Los maps no se pueden copiar directamente, se tiene que hacer mediante un bucle (for)
- Las claves en un map deben ser únicas. Esto significa que no puedes tener dos claves idénticas en un map. Si intentas agregar una clave existente, el valor anterior asociado con esa clave se reemplazará con el nuevo valor.
- Se accede a cada elemento mediante su clave (key)
- Puedes obtener la longitud de un map utilizando la función
len(map).
- Puedes utilizar un loop
fory la sintaxisrangepara iterar sobre los elementos de un map.
- Puedes eliminar elementos de un map utilizando la función
delete(map, clave).
package main
import "fmt"
func main() {
// Crear un map vacío
students := make(map[string]int)
// Agregar elementos al map
students["Juan"] = 18
students["Pedro"] = 25
students["Maria"] = 40
fmt.Println(students)
// Acceder a un valor utilizando una clave
ageJuan := students["Juan"]
fmt.Println("La edad de Juan es:", ageJuan)
// Modificar un valor existente
students["Pedro"] = 55
fmt.Println(students)
// Eliminar elemento del map
delete(students, "Maria")
fmt.Println(students)
// Verificar si una clave existe en el map
ageJose, exists:= students["Jose"]
if exists {
fmt.Println("La edad de Jose es:", ageJose)
} else {
fmt.Println("Jose no está en el map")
}
// Iterar sobre los elementos del map
for name, age := range students {
fmt.Println(name, "tiene", age , "años")
}
}Recuerda que los maps en Go no mantienen un orden específico de los elementos, por lo que el orden en el que se iteran los elementos puede variar en cada ejecución.
Los maps en Go son una herramienta poderosa para trabajar con datos clave-valor, y ofrecen un rendimiento eficiente en la búsqueda y manipulación de datos.
3. Estructuras de control: condicionales y bucles.
- ¿Qué son las estructuras de control?
- Las estructuras de control permiten controlar el flujo de la ejecución del código. Para poder construir programas es necesario el uso de estas de forma que representan la lógica de “que hacer en caso de” y “a donde ir”
- ¿Qué tipos de estructuras de control existen en Go?
- Condicionales
- If: Permite ejecutar un bloque de código si una condición es
true.
- Else: Se utiliza junto con
ifpara ejecutar un bloque de código como alternativa si la condición en elifesfalse.
- Else If: Premite evaluar multiples condiciones en cadena y ejecutar el bloque de código correspondiente a la primera condición que sea verdadera.
- Switch: Permite seleccionar un caso específico de varios posibles, basándose en el valor de una expresión.
If / Else / Else If
La sentencia
ifes la más sencilla de utilizar y se encuentra en casi todos los lenguajes de programación. Representa el paradigma de lo que sucede si una condición se cumple y en combinación con la sentenciaelsepuede especificar un camino alternativo.La sentencia
elseque se puede traducir como “si no…” o “de lo contrario…”, permite definir un bloque de código en caso de que la condiciónifno se cumpla.package main import "fmt" func main() { // Definimos una variable age tipo entero var age int fmt.Println("Cual es tu edad: ") // Capturamos age en el puntero &age // fmt.Scanln toma la dirección de memoria de la variable donde se almacenará el valor ingresado por el usuario fmt.Scanln(&age) // Evaluamos si age es mayor o igual a 18 if age >= 18 { fmt.Println("Eres mayor de edad") } else { fmt.Println("Eres menor de edad") } }Existe casos en los cuales lo que se requiere es poder evaluar múltiples condiciones, no solo dos.
- Anidar un if dentro de otro
- Utilizar
else ifpara cubrir cada una de las condiciones
package main import "fmt" func main() { var movie string fmt.Println("Escribe una categoria: terror | comedia | drama") fmt.Scanln(&movie) if movie == "terror" { fmt.Println("Lista de peliculas de terror") } else if movie == "comedia" { fmt.Println("Lista de peliculas de comedia") } else if movie == "drama" { fmt.Println("Lista de peliculas de comedia") } else { fmt.Println("No existe esa categoría") } }Switch
La sentencia switch es equivalente al uso de múltiples
else if, pero de una forma más ordenada.A diferencia de otros lenguajes de programación en los cuales el condicional
switchrequiere de la palabra reservadabreak, en Go no es necesario usarla.- Formas de utilizar switch
switchcon expresión
switchsin expresión
switchcon múltiples valores en un caso
switchcon tipo de dato.
switchcon fallthrough (fallthrough es usada para transferir el control a la primera declaración del case que está presente inmediatamente después del case que se ha ejecutado. Esta solo se puede usar como la declaración final en una cláusula (al final de cada case).
package main import "fmt" func main() { // Switch con expresión // También se puede declarar la variable primero fruit := "manzana" switch fruit := "manzana"; fruit { case "manzana": fmt.Println("Es una manzana") case "pera": fmt.Println("Es una pera") default: fmt.Println("No es una fruta") } // Es una manzana // Switch sin expresión number := 10 switch { case number < 0: fmt.Println("Es negativo") case number > 0 && number < 100: fmt.Println("Es positivo") default: fmt.Println("Es cero") } // Switch con múltiples valores en un caso day := "lunes" switch day { case "lunes", "martes", "miercoles", "jueves", "viernes": fmt.Println("Es un día laboral") case "sabado", "domingo": fmt.Println("Es un día de fin de semana") default: fmt.Println("No es un día válido") } // Switch con tipo de dato var data interface{} data = "Hola" switch data.(type) { case int: fmt.Println("Es un entero") case string: fmt.Println("Es una cadena") default: fmt.Println("Tipo desconocido") } // Switch con fallthrough days := 42 switch days { case 42: fmt.Println("42") fallthrough case 10: fmt.Println("10") fallthrough case 20: fmt.Println("20") } }Las condicionales son una parte muy importante para cualquier programa sin importar el lenguaje de programación ya que nos permite tomar decisiones basadas en ciertas condiciones
- If: Permite ejecutar un bloque de código si una condición es
- Bucles
- For: Se utilza para repetir N veces un bloque de código o mientras se cumple una condición.
- For…range: Se utiliza para iterar sobre elementos de una estructura de datos, como un
array, unslice, unmapo unstring.
- Break: Permite salir de un bucle de manera prematura.
- Continue: Permite saltar a la siguiente iteración del bucle sin ejecutar del resto de bloque de código.
package main import "fmt" func main() { // Opción 1 [FOR] for i := 0; i < 5; i++ { if i == 2 { continue // Salta a la siguiente iteración si i es igual a 2 } fmt.Println(i) } // Opción 2 [SIMILAR A WHILE] j := 0 for j < 5 { fmt.Println(j) j++ } // Opción 3 [FOR INFINITO] x := 0 for { fmt.Println(x) x++ if x == 5 { break // Rompe el bucle cuando i es igual a 5 } } // Opción 3 [FOR RANGE] numbers := []int{1, 2, 3, 4, 5} for index, value := range numbers { fmt.Println("Índice:", index, "Valor:", value) } }
- Otros
- goto: Permite saltar a una etiqueta específica en el código
- defer: Se utilza para posponer la ejecución de una función hasta que la función que lo rodea termine.
- panic y recover: se utilizan para manejar situaciones excepcionales(
pacnic) y recuperarse de ellas(recover)
- Condicionales
4. Tarea
- Escribe un programa en Go que imprima los números pares del 1 al 20.
// Solución
package main
import "fmt"
func main() {
for i := 1; i <= 20; i++ {
if i%2 == 0 {
fmt.Println(i)
}
}
}- Escribe un programa en Go que imprima los números del 1 al 100, pero para los múltiplos de 3 imprima "Fizz", para los múltiplos de 5 imprima "Buzz" y para los múltiplos de ambos 3 y 5 imprima "FizzBuzz".
// Solución
package main
import "fmt"
func main() {
for i := 1; i <= 100; i++ {
if i%3 == 0 && i%5 == 0 {
fmt.Println("FizzBuzz")
} else if i%3 == 0 {
fmt.Println("Fizz")
} else if i%5 == 0 {
fmt.Println("Buzz")
} else {
fmt.Println(i)
}
}
}5. Recursos
- Structs, Slices, Maps: https://go.dev/tour/moretypes/1
- Control de flujos: https://go.dev/tour/flowcontrol/1
- Arrays and Slices: https://golangbot.com/arrays-and-slices/
- If else statement: https://golangbot.com/if-statement/
- Arrays: https://oregoom.com/go/arrays/
- Slices: https://oregoom.com/go/slices/