One place for hosting & domains

      matrices

      Información sobre matrices y segmentos en Go


      Introducción

      En Go, las matrices y los segmentos son estructuras de datos que consisten en una secuencia ordenada de elementos. Estas colecciones de datos son muy útiles cuando se necesita trabajar con muchos valores relacionados. Permiten mantener en un mismo lugar los datos que deben ir juntos, condensar su código y aplicar los mismos métodos y las mismas operaciones en varios valores a la vez.

      Aunque las matrices y los segmentos de Go son secuencias ordenadas de elementos, existen grandes diferencias entre ambos. Una matriz de Go es una estructura de datos que consta de una secuencia ordenada de elementos con su capacidad definida en el momento de su creación. Una vez que se asigna el tamaño a una matriz, este ya no se puede cambiar. Por otra parte, un segmento es una versión de extensión variable de una matriz, lo que ofrece mayor flexibilidad a los desarrolladores que usan estas estructuras de datos. Los segmentos representan lo que se podría considerar como matrices en otros lenguajes.

      Dadas estas diferencias, existen situaciones específicas en las que se usará uno en vez del otro. Si recién comienza a usar Go, determinar el momento en que se usarán puede ser confuso: aunque la versatilidad de los segmentos los convierte en la opción más apropiada en la mayoría de los casos, existen casos específicos en los que las matrices pueden optimizar el rendimiento de su programa.

      En este artículo se abordarán en detalle las matrices y los segmentos, que le proporcionarán la información necesaria para hacer la elección adecuada cuando tenga que decidirse por uno de estos tipos de datos. Además, verá las formas más comunes de declarar matrices y segmentos, y de trabajar con ellos. En el tutorial se brindará primero una descripción de las matrices y la manera de manipularlas, y luego una explicación de los segmentos y las diferencias entre ellos.

      Matrices

      Las matrices son estructuras de conjuntos de datos con un número determinado de elementos. Debido a que el tamaño de una matriz es estático, la estructura de datos solo debe asignar memoria una vez, a diferencia de una estructura de datos de extensión variable que debe asignar memoria de forma dinámica para que pueda ser mayor o menor en el futuro. Aunque la extensión fija de las matrices puede hacer que sea complicado trabajar con ellas, la asignación de memoria por única vez puede aumentar la velocidad y el rendimiento de su programa. Debido a esto, los desarrolladores suelen usar matrices cuando optimizan programas en instancias en las cuales la estructura de datos nunca necesitará una cantidad variable de elementos.

      Definir una matriz

      Las matrices se definen declarando su tamaño entre corchetes [], seguido del tipo de datos de los elementos. Todos los elementos de una matriz de Go deben tener el mismo tipo de datos. Después del tipo de datos, puede declarar los valores individuales de los elementos de la matriz entre llaves { }.

      A continuación, se muestra el esquema general para declarar una matriz:

      [capacity]data_type{element_values}
      

      Nota: Es importante recordar que con cada declaración de una nueva matriz se crea un tipo distinto. Por lo tanto, aunque [2]int y [3]int tienen elementos enteros, sus extensiones diferentes hacen que sus tipos de datos sean incompatibles.

      Si no declara los valores de los elementos de la matriz, el valor predeterminado es cero, lo cual significa que los elementos de la matriz estarán vacíos. Para los enteros, esto se representa con 0 y para cadenas con una cadena vacía.

      Por ejemplo, la siguiente matriz numbers tiene tres elementos enteros que aún no tienen valor:

      var numbers [3]int
      

      Si imprime numbers, obtendrá el siguiente resultado:

      Output

      [0 0 0]

      Si desea asignar los valores de los elementos cuando cree la matriz, disponga los valores entre llaves. Una matriz de cadenas con valores determinados tiene el siguiente aspecto:

      [4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}
      

      Puede almacenar una matriz en una variable e imprimirla:

      coral := [4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}
      fmt.Println(coral)
      

      Si ejecuta un programa con las líneas anteriores, obtendrá el siguiente resultado:

      Output

      [blue coral staghorn coral pillar coral elkhorn coral]

      Observe que no hay delineación entre los elementos de la matriz cuando se imprime. Esto hace que resulte difícil saber dónde termina un elemento y comienza otro. Debido a esto, a veces es útil usar la función fmt.Printf en su lugar, la cual le puede dar formato a las cadenas antes de imprimirlas en la pantalla. Proporcione el verbo %q con este comando para indicar a la función que disponga los valores entre comillas:

      fmt.Printf("%qn", coral)
      

      Esto dará como resultado lo siguiente:

      Output

      ["blue coral" "staghorn coral" "pillar coral" "elkhorn coral"]

      Ahora cada elemento está entre comillas. El verbo n indica al formateador que añada un salto de línea al final.

      Con una idea general sobre cómo declarar matrices y en qué consisten, ahora podrá aprender a especificar elementos en una matriz con un número de índice.

      Indexar matrices (y segmentos)

      Cada elemento de una matriz (y también de un segmento) puede invocarse de forma individual mediante indexación. Cada elemento corresponde a un número de índice, que es un valor int a partir del número de índice 0 en adelante.

      Usaremos una matriz en los siguientes ejemplos, pero también podría utilizar un segmento, ya que la forma en la que se indexan es igual para ambos.

      Para la matriz coral, el desglose del índice tiene este aspecto:

      “blue coral” “staghorn coral” “pillar coral” “elkhorn coral”
      0 1 2 3

      El primer elemento, la cadena “blue coral”, comienza en el índice 0, y el segmento termina en el índice 3 con el elemento “elkhorn coral”.

      Debido a que cada elemento en un segmento o matriz tiene un número de índice correspondiente, podemos acceder a ellos y manipularlos como a otros tipos de datos secuenciales.

      Ahora, podemos invocar un elemento discreto del segmento haciendo referencia a su número de índice:

      fmt.Println(coral[1])
      

      Output

      staghorn coral

      Los números de índice para este segmento tienen un intervalo 0-3, como se muestra en la tabla anterior. Por lo tanto, para llamar a cualquiera de los elementos individualmente, nos referiremos a los números de índice de la siguiente manera:

      coral[0] = "blue coral"
      coral[1] = "staghorn coral"
      coral[2] = "pillar coral"
      coral[3] = "elkhorn coral"
      

      Si invocamos la matriz coral con cualquier número de índice superior a 3, estará fuera de rango porque no será válido:

      fmt.Println(coral[18])
      

      Output

      panic: runtime error: index out of range

      Al indexar una matriz o un segmento, siempre debe usar un número positivo. A diferencia de algunos lenguajes que le permiten aplicar indexación en sentido inverso con un número negativo, hacer eso en Go producirá un error:

      fmt.Println(coral[-1])
      

      Output

      invalid array index -1 (index must be non-negative)

      Podemos concatenar elementos de cadenas de una matriz o un segmento con otras cadenas usando el operador +:

      fmt.Println("Sammy loves " + coral[0])
      

      Output

      Sammy loves blue coral

      Pudimos concatenar el elemento de la cadena en el índice de número 0 con la cadena “Sammy loves”.

      Con los números de índice que corresponden a elementos dentro de una matriz o segmento, podemos acceder a cada elemento con discreción y trabajar con ellos. Para demostrar esto, a continuación veremos cómo modificar un elemento en un índice determinado.

      Modificar elementos

      Podemos usar la indexación para cambiar elementos dentro de una matriz o segmento configurando un elemento numerado con un índice igual a un valor diferente. Esto nos proporciona un mayor control sobre los datos de nuestros segmentos y matrices, y nos permitirá manipular mediante programación elementos individuales.

      Si queremos cambiar el valor de la cadena del elemento en el índice 1 de la matriz coral, de “staghorn coral” a “foliose coral ”, podemos hacerlo de la siguiente manera:

      coral[1] = "foliose coral"
      

      Ahora cuando imprimamos coral, la matriz será diferente:

      fmt.Printf("%qn", coral)
      

      Output

      ["blue coral" "foliose coral" "pillar coral" "elkhorn coral"]

      Ahora que ya sabe manipular elementos individuales de una matriz o un segmento, veamos algunas funciones que le darán mayor flexibilidad al trabajar con tipos de datos de colección.

      Contar elementos con len()

      En Go, len() es una función integrada creada para ayudarlo a trabajar con matrices y segmentos. Al igual que con las cadenas, puede calcular la extensión de una matriz o un segmento usando len() y pasar la matriz o segmento como parámetro.

      Por ejemplo, para encontrar la cantidad elementos de la matriz coral, utilizaría lo siguiente:

      len(coral)
      

      Si imprime la longitud de la matriz coral, obtendrá el siguiente resultado:

      Output

      4

      Esto proporciona 4 a la extensión de la matriz en el tipo de datos int, lo cual es correcto porque la matriz coral tiene cuatro elementos:

      coral := [4]string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral"}
      

      Si crea una matriz de enteros con más elementos, podrá usar la función len() también:

      numbers := [13]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
      fmt.Println(len(numbers))
      

      Esto daría como resultado lo siguiente:

      Output

      13

      Aunque estas matrices de ejemplo tienen relativamente pocos elementos, la función len() es particularmente útil para determinar la cantidad de elementos disponibles en matrices muy grandes.

      A continuación, veremos cómo añadir un elemento a un tipo de datos de coleccción y a demostrar cómo, debido a la extensión fija de las matrices, añadir estos tipos de datos estáticos provocará un error.

      Añadir elementos con append()

      append() es un método incorporado en Go que añade elementos a un tipo de datos de colección. Sin embargo, este método no funcionará cuando se utilice con una matriz. Como se mencionó anteriormente, la principal diferencia entre las matrices y los segmentos es que el tamaño de una matriz no se puede modificar. Esto significa que si bien se pueden cambiar los valores de los elementos de una matriz, no es posible expandirla o reducirla matriz una vez que se haya definido.

      Analicemos su matriz coral:

      coral := [4]string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral"}
      

      Supongamos que quiere añadir el elemento “black coral” a esta matriz. Si intenta usar la función append() con la matriz escribiendo lo siguiente:

      coral = append(coral, "black coral")
      

      Como resultado, verá un error:

      Output

      first argument to append must be slice; have [4]string

      Para corregir esto, veamos más sobre el tipo de datos de segmento, cómo definir un segmento y cómo realizar la conversión de una matriz a un segmento.

      Segmentos

      Un segmento es un tipo de datos en Go que es una secuencia ordenada mutable o modificable de elementos. Debido a que el tamaño de un segmento es variable, existe mucha mayor flexibilidad al usarlos; al trabajar con colecciones de datos que podrían necesitar expandirse o contraerse en el futuro, utilizar un segmento garantizará que su código no tenga errores cuando intente manipular la extensión del conjunto. En la mayoría de los casos, esta mutabilidad hace que valga la pena la posible reasignación de memoria que a veces requieren los segmentos en comparación con las matrices. Cuando necesite almacenar muchos elementos o iterarlos y desee ser capaz de modificar fácilmente esos elementos, es probable que desee trabajar con el tipo de datos de segmento.

      Definir un segmento

      Los segmentos se definen declarando el tipo de datos precedido por un conjunto vacío de corchetes ([]) y una lista de elementos entre llaves ({}). Observará que, a diferencia de las matrices que requieren un int entre los corchetes para declarar una extensión específica, un segmento no contiene nada entre corchetes, lo que representa su extensión variable.

      Crearemos un segmento que contenga elementos del tipo de datos de cadena:

      seaCreatures := []string{"shark", "cuttlefish", "squid", "mantis shrimp", "anemone"}
      

      Cuando imprimimos el segmento, podemos observar los elementos que están en el segmento:

      fmt.Printf("%qn", seaCreatures)
      

      Esto dará como resultado lo siguiente:

      Output

      ["shark" "cuttlefish" "squid" "mantis shrimp" "anemone"]

      Si desea crear un segmento de una extensión determinada sin completar aún los elementos del grupo, puede usar la función integrada make():

      oceans := make([]string, 3)
      

      Si imprimiera este segmento, obtendría lo siguiente:

      Output

      ["" "" ""]

      Si desea asignar previamente la memoria a una capacidad determinada, puede pasar un tercer argumento a make():

      oceans := make([]string, 3, 5)
      

      Con esto, se crearía un segmento con una extensión de 3, valor cero y una capacidad previamente asignada de 5 elementos.

      Ahora sabe cómo declarar un segmento. Sin embargo, esto aún no resuelve el error que se produjo antes con la matriz coral. Para usar la función append() con coral, primero tendrá que aprender a quitar secciones de una matriz.

      Dividir matrices en segmentos

      Usando números de índice para determinar los puntos de inicio y final, puede invocar un apartado de los valores dentro de una matriz. Esto se conoce como segmentación de la matriz y puede hacerlo creando un rango de números de índice separados por dos puntos con la forma [first_index:second_index]. Sin embargo, es importante observar que cuando se divide una matriz, el resultado es un segmento, no una matriz.

      Digamos que desea imprimir solo los elementos centrales de la matriz coral, sin el primer y último elemento. Puede realizarlo creando un segmento que empiece en el índice 1 y termine justo antes del índice 3:

      fmt.Println(coral[1:3])
      

      La ejecución de un programa con esta línea produciría el siguiente resultado:

      Output

      [foliose coral pillar coral]

      Al crear una división, como en [1:3], el primer número marca el inicio del segmento (incluido) y el segundo es la suma del primer número y del número total de elementos que desea obtener:

      array[starting_index : (starting_index + length_of_slice)]
      

      En este caso, llamó al segundo elemento (o índice 1) como el punto de inicio y llamó dos elementos en total. Así es como se vería el cálculo:

      array[1 : (1 + 2)]
      

      Esto le permitió obtener la siguiente notación:

      coral[1:3]
      

      Si desea establecer el inicio o el final de la matriz como un punto de inicio o final de la división, puede omitir uno de los números de la sintaxis array[first_index:second_index]. Por ejemplo, si desea imprimir los primeros tres elementos de la matriz coral, que serían “blue coral”, “foliose coral” y “pillar coral”, puede hacerlo al escribiendo lo siguiente:

      fmt.Println(coral[:3])
      

      Con esto se imprimirá lo siguiente:

      Output

      [blue coral foliose coral pillar coral]

      Esto imprimió el principio de la matriz y se detuvo justo antes del índice 3.

      Para incluir todos los elementos al final de una matriz, invertiría la sintaxis:

      fmt.Println(coral[1:])
      

      De esto surgiría el siguiente segmento:

      Output

      [foliose coral pillar coral elkhorn coral]

      En esta sección se explicó la manera de invocar partes individuales de una matriz mediante la eliminación de apartados. A continuación, aprenderá a usar la segmentación para convertir matrices enteras en segmentos.

      Convertir una matriz en un segmento

      Si creó una matriz y decide que necesita que tenga una extensión variable, puede convertirla en un segmento. Para convertir una matriz en un segmento, utilice el proceso de segmentación que aprendió en el paso Dividir matrices en segmentos de este tutorial. Como excepción, esta vez seleccione el segmento completo omitiendo los dos números de índice que determinan los extremos:

      coral[:]
      

      Tenga en cuenta que no puede convertir la variable coral en un segmento en sí, ya que una vez que una variable se define en Go, no se puede cambiar su tipo. Para evitar esto, puede copiar todo el contenido de la matriz en una nueva variable como segmento:

      coralSlice := coral[:]
      

      Si imprimió coralSlice, obtendría el siguiente resultado:

      Output

      [blue coral foliose coral pillar coral elkhorn coral]

      Ahora, intente añadir el elemento black coral como lo hizo en la sección de matrices, usando append() con el segmento recién convertido:

      coralSlice = append(coralSlice, "black coral")
      fmt.Printf("%qn", coralSlice)
      

      Esto mostrará el segmento con el elemento añadido:

      Output

      ["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral"]

      También podemos añadir más de un elemento en una sola instrucción append():

      coralSlice = append(coralSlice, "antipathes", "leptopsammia")
      

      Output

      ["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia"]

      Para combinar dos segmentos, puede usar append(), pero debe ampliar el segundo argumento para la anexión usando la sintaxis de expansión ...:

      moreCoral := []string{"massive coral", "soft coral"}
      coralSlice = append(coralSlice, moreCoral...)
      

      Output

      ["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]

      Ahora que aprendió a anexar un elemento a su segmento, veremos la manera de eliminar uno.

      Eliminar un elemento de un segmento

      A diferencia de otros lenguajes, Go no cuenta con funciones incorporadas para eliminar un elemento de un segmento. Los elementos deben eliminarse de un segmento mediante segmentación.

      Para eliminar un elemento, debe retirar los elementos anteriores y posteriores a este, luego anexar estos dos nuevos segmentos sin el elemento que desee eliminar.

      Si i es el índice del elemento que se eliminará, el formato de este proceso sería el siguiente:

      slice = append(slice[:i], slice[i+1:]...)
      

      De coralSlice, eliminaremos el elemento “elkhorn coral”. Este elemento se encuentra en la posición de índice 3.

      coralSlice := []string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral", "black coral", "antipathes", "leptopsammia", "massive coral", "soft coral"}
      
      coralSlice = append(coralSlice[:3], coralSlice[4:]...)
      
      fmt.Printf("%qn", coralSlice)
      

      Output

      ["blue coral" "foliose coral" "pillar coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]

      Ahora, el elemento de la posición de índice 3, la cadena “elkhorn coral”, ya no se encuentra en nuestro segmento coralSlice.

      También podemos eliminar un rango utilizando el mismo enfoque. Supongamos que no solo queremos eliminar el elemento “elkhorn coral”, sino también “ black coral ” y “ antipathes ”. Podemos usar un rango en la expresión para lograr esto:

      coralSlice := []string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral", "black coral", "antipathes", "leptopsammia", "massive coral", "soft coral"}
      
      coralSlice = append(coralSlice[:3], coralSlice[6:]...)
      
      fmt.Printf("%qn", coralSlice)
      

      En este código, se eliminarán los índices 3, 4 y 5 del segmento:

      Output

      ["blue coral" "foliose coral" "pillar coral" "leptopsammia" "massive coral" "soft coral"]

      Ahora que sabe añadir elementos y eliminarlos de un segmento, veremos la manera de medir la cantidad de datos que un segmento puede contener en un momento determinado.

      Medir la capacidad de un segmento con cap()

      Debido a que los segmentos tienen una extensión variable, el método len() no es la mejor opción para determinar el tamaño de este tipo de datos. En lugar de esto, puede usar la función cap() para conocer la capacidad de un segmento. Esto le mostrará la cantidad de elementos que un segmento puede contener, lo cual se determina por la cantidad de memoria ya asignada al segmento.

      Nota: Debido a que la extensión y la capacidad de una matriz siempre son iguales, la función cap() no funcionará en matrices.

      Una aplicación común para cap() consiste en crear un segmento con un número de elementos predeterminado y luego completar estos elementos mediante programación. Esto evita asignaciones innecesarias que podrían producirse al usar append() para añadir elementos que superen la capacidad actualmente asignada.

      Consideraremos una situación en la que queramos realizar una lista de números del 0 a 3. Podemos usar append() en un bucle para hacerlo, o primero podemos asignar previamente el segmento y usar cap() en un bulce para llenar los valores.

      Primero, analicemos el uso de append():

      numbers := []int{}
      for i := 0; i < 4; i++ {
          numbers = append(numbers, i)
      }
      fmt.Println(numbers)
      

      Output

      [0 1 2 3]

      En este ejemplo, creamos un segmento y luego un bucle for que haría cuatro iteraciones. Cada iteración anexó el valor actual de la variable del bucle i en el índice del segmento numbers. Sin embargo, esto podría provocar asignaciones de memoria innecesarias que podrían ralentizar su programa. Al añadir a un segmento vacío, cada vez que realice una invocación para una anexión, el programa verificará la capacidad del segmento. Si el elemento añadido hace que el segmento supere esta capacidad, el programa asignará memoria adicional para compensarlo. Esto crea una sobrecarga adicional en su programa y puede ralentizar la ejecución.

      Ahora, completaremos el segmento sin usar append() asignando previamente una extensión y una capacidad determinadas:

      numbers := make([]int, 4)
      for i := 0; i < cap(numbers); i++ {
          numbers[i] = i
      }
      
      fmt.Println(numbers)
      
      

      Output

      [0 1 2 3]

      En este ejemplo, usamos make() para crear un segmento e indicar a este que asigne previamente 4 elementos. Luego usamos la función cap() en el bucle para iterar cada elemento con cero y completar cada uno hasta que cada uno alcanzara la capacidad previamente asignada. En cada bucle, dispusimos el valor actual de la variable de bucle i en el índice del segmento numbers.

      Si bien las estrategias append() y cap() tienen un funcionamiento equivalente, en el ejemplo de cap() se evita cualquier asignación de memoria adicional que hubiera sido necesaria si se utilizaba la función append().

      Crear segmentos multidimensionales

      También puede definir segmentos que consistan en otros segmentos como elementos y disponga cada una de las listas entre llaves, dentro de las llaves más amplias del segmento principal. Los grupos de segmentos como estos se conocen como segmentos multidimensionales. Se puede pensar que representan coordenadas multidimensionales; por ejemplo, un conjunto de cinco segmentos de seis elementos de extensión cada uno podría representar una cuadrícula de bidimensional con una extensión horizontal de cinco y una altura vertical de seis.

      Analicemos el siguiente segmento multidimensional:

      seaNames := [][]string{{"shark", "octopus", "squid", "mantis shrimp"}, {"Sammy", "Jesse", "Drew", "Jamie"}}
      

      Para acceder a un elemento dentro de este segmento, debemos usar varios índices, uno para cada dimensión de la construcción:

      fmt.Println(seaNames[1][0])
      fmt.Println(seaNames[0][0])
      

      En el código anterior, identificamos primero el elemento del índice 0 del segmento del índice 1, luego indicamos el elemento del índice 0 del segmento del índice 0. Con esto, se mostrará lo siguiente:

      Output

      Sammy shark

      Los siguientes son los valores de índice para el resto de los elementos individuales:

      seaNames[0][0] = "shark"
      seaNames[0][1] = "octopus"
      seaNames[0][2] = "squid"
      seaNames[0][3] = "mantis shrimp"
      
      seaNames[1][0] = "Sammy"
      seaNames[1][1] = "Jesse"
      seaNames[1][2] = "Drew"
      seaNames[1][3] = "Jamie"
      

      Al trabajar con segmentos multidimensionales, es importante tener en cuenta que deberá hacer referencia a más de un número de índice para poder acceder a elementos específicos dentro del segmento anidado correspondiente.

      Conclusión

      A través de este tutorial, aprendió aspectos básicos del uso de matrices y segmentos en Go. Realizó varios ejercicios para demostrar que las matrices tienen una extensión fija, mientras que los segmentos no, y descubrió el efecto de esta diferencia en el uso de estas estructuras de datos en cada caso.

      Para continuar estudiando estructuras de datos en Go, consulte nuestro artículo Información sobre mapas en Go o toda la serie Cómo aplicar codificación en Go.



      Source link