17 - Estructura de datos tipo slices

Hemos trabajado en los conceptos anteriores con la estructura de datos tipo array. Un array es muy adecuado cuando conocemos antes de compilar el programa la cantidad de elementos a almacenar.

Hay muchas situaciones donde no sabemos cuantas componentes reservar para un array, en estas situaciones tenemos que utilizar probablemente slices.

Un slice permite almacenar un conjunto de datos del mismo tipo pero la cantidad de elementos de la misma puede crecer a lo largo de la ejecución del programa.

Problema 1:

Confeccionar un programa que solicite al inicio cuantos importes de facturas se ingresaran por teclado. Almacenar los datos en un slice, imprimirlo y sumar todos sus elementos.

Programa: ejercicio081.go

package main

import "fmt"

func main() {
    var cantidad int
    var suma float64
    fmt.Print("Cuantas facturas se procesarán:")
    fmt.Scan(&cantidad)
    facturas := make([]float64, cantidad)
    for f :=0; f < len(facturas); f++ {
        fmt.Print("Ingrese importe de la factura:")
        fmt.Scan(&facturas[f])
        suma = suma + facturas[f]
    }
    fmt.Println("Listado completo de facturas ingresadas")
    fmt.Println(facturas)
    fmt.Println("Importe total de todas las facturas:", suma)    
}

Hay varias formas de crear slices en el lenguaje Go. En este problema hemos creado el slice llamado facturas mediante la función make:

    facturas := make([]float64, cantidad)

La función make en una de sus formas tiene dos parámetros, el primero indica el tipo de dato que almacena el slice y el segundo cuanto espacio se reserva. Pasamos la variable cantidad que será el número de facturas que ingresará el operador de nuestro programa.

Para acceder a las componentes de un slice es idéntico a como trabajamos con un array, es decir mediante un subíndice hacemos referencia a cada componente:

    for f :=0; f < len(facturas); f++ {
        fmt.Print("Ingrese importe de la factura:")
        fmt.Scan(&facturas[f])
        suma = suma + facturas[f]
    }

También mediante la función len podemos saber cuanto espacio tenemos reservado para el slice.

La función Println del paquete fmt muestra todo el contenido del slice:

    fmt.Println("Listado completo de facturas ingresadas")
    fmt.Println(facturas)

Variar la cantidad de elementos del slice a lo largo del programa.

Hay una función llamada append que añade al final una nueva componente al slice.

Problema 2:

Emplear distintas formas para definr slices en Go.

Programa: ejercicio082.go

package main

import "fmt"

func main() {
    var slice1 []int
    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))
    slice1 = append(slice1, 100)
    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))
    slice1 = append(slice1, 200)
    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))
    slice1 = append(slice1, 300)
    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))    

    slice2 := make ([]int, 2)
    slice2[0]=100
    slice2[1]=200
    slice2 = append(slice2, 300)
    fmt.Println("Elementos del slice2", slice2)
    fmt.Println("Cantidad de componentes del slice2", len(slice2))

    slice3 := []int{100, 200}
    slice3 = append(slice3, 300)
    fmt.Println("Elementos del slice3", slice3)
    fmt.Println("Cantidad de componentes del slice3", len(slice3))
}

El resultado de ejecutar el programa es:

crear slices en go

Podemos definir un slice vacío con la misma sintaxis que vimos para definir un array pero sin indicar entre corchetes la cantidad de elementos (esa diferencia de sintaxis le permite al compilador crear un slice o un array):

    var slice1 []int

Si imprimimos el slice1 veremos corchetes abiertos y cerrados que nos indica que no tiene elementos y la función len nos retorna un cero:

    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))

Mediante la función append podemos agregar una componente al final del slice, debemos pasar como parámetros el slice propiamente dicho y el valor a insertar. La función append retorna el slice modificado:

    slice1 = append(slice1, 100)
    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))

Como vemos al imprimir ahora el slice1 tiene un elemento con el valor 100 y la función len retorna un 1.

A medida que llamamos a la función append el slice1 crece:

    slice1 = append(slice1, 200)
    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))
    slice1 = append(slice1, 300)
    fmt.Println("Elementos del slice1", slice1)
    fmt.Println("Cantidad de componentes del slice1", len(slice1))    

La segunda forma de crear un slice y muy conveniente cuando sabemos de antemano cual será el tamaño inicial de slice es el empleo de la función make:

    slice2 := make ([]int, 2)
    slice2[0]=100
    slice2[1]=200
    slice2 = append(slice2, 300)
    fmt.Println("Elementos del slice2", slice2)
    fmt.Println("Cantidad de componentes del slice2", len(slice2))

El slice2 se crea y reserva espacio para 2 enteros. Luego accedemos a sus elementos por medio de un subíndice. Si queremos en algún momento de nuestro programa que el slice2 siga creciendo llamamos de ahí en más a la función append.

Una tercer forma de crear un slice es en el momento que lo definimos indicar entre llaves los valores a almacenar, en nuestro ejemplo el slice3 reserva espacio para 2 elementos y lo carga con los valores 100 y 200:

    slice3 := []int{100, 200}

También como en los casos anteriores si necesitamos más espacio podemos añadir mediante la llamada a la función append:

    slice3 = append(slice3, 300)
    fmt.Println("Elementos del slice3", slice3)
    fmt.Println("Cantidad de componentes del slice3", len(slice3))

Problema 3:

Confeccionar un programa que permita almacenar en dos slices los nombres de productos y sus precios (hacer que en las componentes de los mísmos subíndices se guarden datos relacionados)
Cuando inicia el programa solicitar la cantidad de productos a cargar.
Mostrar los datos de los dos slices

Programa: ejercicio083.go

package main

import "fmt"

func main() {
    var cantidad int
    fmt.Print("Cuantos productos ingresará:")
    fmt.Scan(&cantidad)
    productos := make([]string, cantidad)
    precios := make([]float64, cantidad)
    for f := 0; f < len(productos); f++ {
        fmt.Print("Ingrese nombre del producto:")
        fmt.Scan(&productos[f])
        fmt.Print("Ingrese el precio de dicho producto:")
        fmt.Scan(&precios[f])
    }
    fmt.Println("Listado de todos los datos ingresados")
    for f := 0; f < len(productos); f++ {
        fmt.Println(productos[f], "-", precios[f])
    }    
}

Un resultado particular de ejecutar este programa es:

crear slices en go

Primero debe quedar claro que para resolver este problema necesitamos slices ya que el tamaño de las estructuras de datos variará en cada ejecución del programa (el operador puede ingresar cualquier cantidad de artículos)

Creamos dos slices llamados productos y precios, uno de tipo string y otro de tipo float64:

    productos := make([]string, cantidad)
    precios := make([]float64, cantidad)

La cantidad de elementos de los slices depende del contenido de la variable cantidad.

Mediante un for recorremos y cargamos los dos slices y para que estén relacionados uno y el otro en las mismas posiciones de cada slice guardamos en uno el nombre del producto y en el slice paralelo su precio:

    for f := 0; f < len(productos); f++ {
        fmt.Print("Ingrese nombre del producto:")
        fmt.Scan(&productos[f])
        fmt.Print("Ingrese el precio de dicho producto:")
        fmt.Scan(&precios[f])
    }

Queda finalmente mostrar los contenidos de los dos slices:

    fmt.Println("Listado de todos los datos ingresados")
    for f := 0; f < len(productos); f++ {
        fmt.Println(productos[f], "-", precios[f])
    }    

Problema propuesto

  • Definir un slice vacío. En una estructura repetitiva ingresar enteros y añadirlos al slice. Cuando se ingrese el número -1 finalizar la carga y mostrar cuantos números ingresados son mayor al promedio de los valores ingresados.
Solución
ejercicio084.go

package main

import "fmt"

func main() {
    var slice1 []int
    var valor int
    suma := 0
    for {
        fmt.Print("Ingrese un entero (-1 para finalizar):")
        fmt.Scan(&valor)
        if valor==-1 {
            break
        }
        slice1 = append(slice1, valor)
        suma = suma + valor
    }
    promedio := suma / len(slice1)
    mayorProm := 0
    for x := 0; x < len (slice1); x++ {
        if slice1[x] > promedio {
            mayorProm++
        }
    }
    fmt.Println("El slice completo es")
    fmt.Println(slice1)
    fmt.Println("Promedio:", promedio)
    fmt.Println("La cantidad de valores mayores al promedio son:", mayorProm)
}