Suma de subconjuntos con Go

¿De qué trata el programa suma de subconjuntos en Go?

El programa recibe N cantidad de números enteros, los almacena en un slice o array, divide esa lista en dos subconjuntos, suma los valores de cada uno y al final del proceso el programa le dirá al usuario si la suma del subconjunto 1 es igual a la suma del subconjunto 2.

package main 
 
import ( 
    "fmt" 
)

func capturar(tam uint32) []uint32 { 
    conjunto := make([]uint32, 0) 
    var sb, num uint32 
    for sb = 0; sb < tam; sb++ { 
        fmt.Printf("Ingrese el numor #%d: ", sb + 1) 
        fmt.Scanf("%d", &num) 
        conjunto = append(conjunto, num) 
    } 
    return conjunto 
} 

func sumar(conjunto []uint32, tam uint32) (uint32, uint32) { 
    var sumsub1, sumsub2, reban uint32 = 0, 0, 0 
    var sb1, sb2 uint32 

    reban = uint32(len(conjunto[0:uint32(len(conjunto) / 2)])) 
    for sb1 = 0; sb1 < reban; sb1++ { 
        sumsub1 = sumsub1 + conjunto[sb1] 
    } 
    for sb2 = reban; sb2 < tam; sb2++ { 
        sumsub2 = sumsub2 + conjunto[sb2] 
    } 
    return sumsub1, sumsub2 
} 

func main() { 
    conjunto := make([]uint32, 0) 
    var tam, sumsub1, sumsub2 uint32 

    fmt.Print("Ingrese el numero de valores del conjunto: ") 
    fmt.Scanf("%d", &tam) 

    fmt.Println() 
    conjunto = capturar(tam) 
    sumsub1, sumsub2 = sumar(conjunto, tam) 

    if sumsub1 == sumsub2 { 
        fmt.Println("La suma de los subconjuntos da el mismo resultado!") 
    } else { 
        fmt.Println("La suma de los subconjuntos no da el mismo resultado!") 
    } 
}

Está un poco largo el programa pero contiene varios conceptos interesantes con el lenguaje de programación Go, así que veremos un paso a paso enmarcando lo más importante.

Sentencias package e import, nombrar e importar paquetes:
package main 
 
import ( 
    "fmt" 
)

Go es un lenguaje bastante estricto y es obligatorio asignar un nombre de paquete a cada archivo o grupo de archivos fuentes, este nombre se asigna usando la sentencia package seguida por el nombre, en este caso es main; una regla obligatoria a cumplir es que todo programa no solo debe tener una función main() sino que también el paquete principal debe llamarse main. Paso a seguir es importar los paquetes que necesitemos, en este caso solo requerimos el paquete fmt.

Primera función, capturando datos del usuario:
func capturar(tam uint32) []uint32 { 
    conjunto := make([]uint32, 0) 
    var sb, num uint32 
    for sb = 0; sb < tam; sb++ { 
        fmt.Printf("Ingrese el numor #%d: ", sb + 1) 
        fmt.Scanf("%d", &num) 
        conjunto = append(conjunto, num) 
    } 
    return conjunto 
}

Antes de empezar, vale la pena recordar que toda función en Go se crea usando la palabra clave funcseguida por el nombre de la función y por último, pero de forma opcional, se añade el tipo o tipos de retorno en caso que dicha función vaya a devolver uno o más resultados. Una vez explicada esta parte, continuamos.

La función capturar tiene una labor básica pero importante ya que será la encargada de solicitar los datos al usuario y almacenarlos en alguna variable, ella recibe un único parámetro de tipo int de 32 bits sin signo, es decir, solo números positivos. Esta función retorna un solo resultado el cual es de tipo array que almacenará elementos de tipo int de 32 bits como se puede evidenciar después de los paréntesis, []uint32.

Declaración de variables y Slice (array dinamico):
conjunto := make([]uint32, 0)
var sb, num uint32

En la primera línea se declara una variable de tipo Slice usando inferencia de tipos, los Slices se crean en el lenguaje de programación Go con la función make(); recibe dos argumentos obligatorios y uno opcional, el primer argumento corresponde a un array con su tipo, en este caso es uint32 (almacena números positivos); el segundo argumento es la longitud del Slice, si el argumento es cero (0) se crea un Slice vacío [] y no hay problema alguno ya que los Slices son dinámicos y se expanden con cada elemento almacenado.




El parámetro opcional corresponde a la capacidad del Slice, es la cantidad de elementos del array subyacente. En la segunda línea se declaran 2 variables de tipo uint32, son variables que almacenan números positivos.

Bucle for(), desde 0 hasta tam recolectando datos:
for sb = 0; sb < tam; sb++ {
    fmt.Printf("Ingrese el numor #%d: ", sb+1)
    fmt.Scanf("%d", &num)
    conjunto = append(conjunto, num)
}

El bucle for va a iterar desde 0 hasta que la variable de inicialización sea menor a tam con el fin de recolectar los elementos que van a ser almacenados en un Slice creado y explicado anteriormente, estos elementos serán ingresados por teclado. Al interior del bucle for() se puede observar que hay 3 líneas de código.

La primera solicita al usuario que ingrese un elemento y los va enumerando desde 1 hasta N con la expresión sb+1 usando la función Printf(), esta función formatea las salida de datos en pantalla al estilo del lenguaje C pero algunas diferencias; la segunda línea deja en espera la terminal hasta que el usuario ingrese los datos por teclado y presione Enter, una vez hecho esto, la información ingresada por el usuario se almacena en la variable num pasada por referencia usando el signo &; la función Scanf() formatea la entrada al estilo de C.

Función append(), añadiendo elementos al Slice:
conjunto = append(conjunto, num)

Para añadir elementos a un Slice se usa la función append(); recibe como primer argumento el Slice al cual se le va a agregar el elemento, como segundo argumento recibe el elemento o elementos que serán agregados: append(Slice, elem1, elem2, …, elemn). Para este caso solo se va a ir agregando el elemento almacenado en la variable num con cada iteración. Hay que indicar que la función append() retorna un Slice y este debe ser almacenado en una variable, es común almacenarlo en la misma variable Slice, en este caso es conjunto.

Devolviendo un resultado:
return conjunto

La función capturar retorna un valor tipo de tipo []uint32 como se explicó anteriormente, esto quiere decir que obligatoriamente la variable que acompaña la sentencia return debe ser de tipo Slice o Array. En la línea anterior se retorna un Slice llamado conjunto, dicha variable contiene todos los elementos ingresados por el usuario en el bucle for() explicado anteriormente.

Segunda función, sumando subconjuntos:
func sumar(conjunto []uint32, tam uint32) (uint32, uint32) {
    var sumsub1, sumsub2, reban uint32 = 0, 0, 0
    var sb1, sb2 uint32

    reban = uint32(len(conjunto[0:uint32(len(conjunto)/2)]))
    for sb1 = 0; sb1 < reban; sb1++ {
        sumsub1 = sumsub1 + conjunto[sb1]
    }
    for sb2 = reban; sb2 < tam; sb2++ {
        sumsub2 = sumsub2 + conjunto[sb2]
    }
    return sumsub1, sumsub2
}

Descripción: La función sumar es la encargada de dividir el conjunto en 2 subconjuntos por mitad, luego sumará los elementos de cada mitad guardando el valor en una variable diferente para cada subconjunto.


En las primeras dos líneas al interior de la función se declaran 5 variables de tipo uint32, son variables de 32 bits que almacenan números positivos; pero solo en la primera línea se inicializan 3 variables usando lo que se conoce como asignación múltiple, otro detalle en esta asignación es que se puede omitir el tipo de dato ya que Go siempre que pueda inferir desde el contexto el tipo de dato de cada variable.

Slicing, dividiendo en dos un Slice en 2:
reban = uint32(len(conjunto[0:uint32(len(conjunto)/2)]))

Slicing en programación básicamente es rebanar un array dinamico o estatico para extraer rangos de elementos que se encuentran almacenados allí. En este fragmento de código se ven conceptos nuevos.

La variable reban que es de tipo uint32 va a almacenar el resultado de rebanar el Slice en 2; sin embargo, las operaciones que se realizan son de tipo int, esto nos genera un error en tiempo de compilación ya que ambos tipos son incompatibles y en Go no hay conversiones implícitas. La solución para este error es sencilla, solo se debe usar la expresión uint32(varint), esto convierte el valor de varint en un tipo uint32.
La función len() retorna la longitud del Slice, aquí se realizan dos operaciones; la primera obtiene la longitud del Slice, luego la divide en 2 y por último, el resultado es convertido a uint32. La expresión Slice[0:N] extrae los elementos en el rango desde 0 hasta N sin incluir el último elemento; la segunda obtiene la longitud de la rebanada obtenida del Sice y la convierte en uint32.

Bucles for(), sumando los elementos de los subconjunto:
for sb1 = 0; sb1 < reban; sb1++ {
    sumsub1 = sumsub1 + conjunto[sb1]
}
for sb2 = reban; sb2 < tam; sb2++ {
    sumsub2 = sumsub2 + conjunto[sb2]
}

Este es el fragmento de código más sencillo de la función sumar, el primer bucle for() va a iterar desde 0 hasta que sbn1 sea menor que reban, obtiene el elemento en el índice sbn1, luego realiza la suma entre sumsub1 y el elemento en el índice sbn1, y asigna el resultado a sumsub1; el segundo bucle for() va a iterar desde reban hasta que sbn2 sea menor que tam y por último se realiza una operación de suma igual que el primer bucle.

Sentencia return, devolviendo múltiples resultados:
return sumsub1, sumsub2

En Go, las funciones pueden devolver múltiples resultados agrupando los tipos entre paréntesis. La función sumar tiene la expresion (uint32, uint32), lo que nos indica que devuelve dos resultados y por lo tanto la sentencia return debe ir acompañada por dos valores separados por «,» como se evidencia en el fragmento de código anterior en donde se devuelve el resultado de sumsub1 y sumsub2.

Función main():

func main() {
    conjunto := make([]uint32, 0)
    var tam, sumsub1, sumsub2 uint32

    fmt.Print("Ingrese el numero de valores del conjunto: ")
    fmt.Scanf("%d", &tam)

    fmt.Println()
    conjunto = capturar(tam)
    sumsub1, sumsub2 = sumar(conjunto, tam)

    if sumsub1 == sumsub2 {
        fmt.Println("La suma de los subconjuntos da el mismo resultado!")
    } else {
        fmt.Println("La suma de los subconjuntos no da el mismo resultado!")
    }
}

En la función main() se observan varias líneas de código de conceptos ya explicados arriba como los son los Slices, declaración de variables, imprimir en pantalla y capturar datos ingresados por teclado, así que los omitiré para no caer en redundancias aburridas.


Sin embargo, se ven 2 líneas de código nuevas; en la línea 9 se usa la variable conjunto que es de tipo Slice ¿por qué? esto se debe a que la función capturar retorna un Slice como resultado y debe ser almacenado en una variable del mismo tipo; en la línea 10 se usan 2 variables separadas por «,» ¿por qué? esto se debe a que la función sumar devuelve 2 resultados, simétricamente se requieren 2 variables para recibir cada resultado. La sentencia if() verifica la igualdad y le indica al usuario si la suma de los subconjuntos son o no son iguales.

Añadir un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *