values form crear array arrays go slice

arrays - form - ¿Por qué tener matrices en Go?



javascript get form values (4)

Cada conjunto podría ser un segmento, pero no todos los segmentos podrían ser un conjunto. Si tiene un tamaño de colección fijo, puede obtener una mejora de rendimiento menor al usar una matriz. Como mínimo, ahorrará el espacio ocupado por el encabezado del sector.

Entiendo la diferencia entre arrays y slices en Go. Pero lo que no entiendo es por qué es útil tener matrices en absoluto. ¿Por qué es útil que una definición de tipo de matriz especifique una longitud y un tipo de elemento? ¿Por qué cada "matriz" que usamos no puede ser una porción?


Hay más en las arrays que la longitud fija: son comparable y son valores (no tipos de referencia o puntero).

Existen innumerables ventajas de los arreglos sobre los segmentos en ciertas situaciones, todos juntos más que justificar la existencia de conjuntos (junto con los segmentos). A verlos. (Ni siquiera estoy contando que las matrices sean los bloques de construcción de las rebanadas).

1. Ser comparable significa que puedes usar matrices como claves en los mapas , pero no cortes. Sí, podría decir ahora que por qué no hacer comparables rebanadas entonces, para que esto solo no justifique la existencia de ambas. La igualdad no está bien definida en los sectores. Preguntas frecuentes: ¿Por qué los mapas no permiten cortes como claves?

No implementan la igualdad porque la igualdad no está bien definida en tales tipos; Existen múltiples consideraciones que involucran la comparación superficial vs. profunda, la comparación de puntero versus valor, cómo tratar con los tipos recursivos, etc.

2. Las matrices también pueden brindarle una mayor seguridad en tiempo de compilación , ya que los límites del índice se pueden verificar en tiempo de compilación (la longitud de la matriz debe evaluarse a una constant no negativa representable por un valor de tipo int ):

s := make([]int, 3) s[3] = 3 // "Only" a runtime panic: runtime error: index out of range a := [3]int{} a[3] = 3 // Compile-time error: invalid array index 3 (out of bounds for 3-element array)

3. También pasar o asignar valores de matriz implícitamente hará una copia de toda la matriz, por lo que se "separará" del valor original. Si pasa un segmento, seguirá haciendo una copia pero solo del encabezado del segmento, pero el valor del segmento (el encabezado) apuntará a la misma matriz de respaldo. Esto puede o no ser lo que quieres. Si desea "separar" un segmento del "original", debe copiar explícitamente el contenido, por ejemplo, con la función incorporada copy() en un nuevo segmento.

a := [2]int{1, 2} b := a b[0] = 10 // This only affects b, a will remain {1, 2} sa := []int{1, 2} sb := sa sb[0] = 10 // Affects both sb and sa

4. Además, dado que la longitud de la matriz es parte del tipo de matriz, las matrices con diferente longitud son tipos distintos . Por un lado, esto puede ser un "dolor en el culo" (por ejemplo, si escribe una función que toma un parámetro de tipo [4]int , no puede usar esa función para tomar y procesar una matriz de tipo [5]int ) , pero esto también puede ser una ventaja: puede usarse para especificar explícitamente la longitud de la matriz que se espera. Por ejemplo, si desea escribir una función que tome una dirección IPv4, se puede modelar con el [4]byte tipo [4]byte . Ahora tiene una garantía en tiempo de compilación de que el valor pasado a su función tendrá exactamente 4 bytes, ni más ni menos (que de todos modos sería una dirección IPv4 no válida).

5. En relación con lo anterior, la longitud de la matriz también puede servir para fines de documentación . Un [4]byte tipo [4]byte documenta correctamente que IPv4 tiene 4 bytes. Una variable rgb de tipo [3]byte indica que hay 1 byte para cada componente de color. En algunos casos, incluso se saca y está disponible, documentado por separado; por ejemplo en el paquete crypto/md5 : md5.Sum() devuelve un valor de tipo [Size]byte donde md5.Size es una constante que es 16 : la longitud de una suma de comprobación MD5.

6. También son muy útiles cuando se planifica el diseño de memoria de los tipos de estructura , vea la respuesta de JimB aquí, y esta respuesta con mayor detalle y un ejemplo de la vida real .

7. Además, dado que los sectores son encabezados y (casi) siempre se pasan tal cual (sin punteros), la especificación del lenguaje es más restrictiva con respecto a los punteros a sectores que a los matrices . Por ejemplo, la especificación proporciona múltiples shorthands para operar con punteros a matrices, mientras que el mismo da un error en tiempo de compilación en caso de cortes (porque es raro usar punteros a cortes, si todavía quiere / tiene que hacerlo, debe ser explícito sobre cómo manejarlo; lea más en esta respuesta ).

Tales ejemplos son:

  • Cortar un puntero p en la matriz: p[low:high] es una abreviatura de (*p)[low:high] . Si p es un puntero para cortar, este es un error en tiempo de compilación ( especificación: Expresiones de corte).

  • Indexación de un puntero p a la matriz: p[i] es una abreviatura de (*p)[i] . Si p es puntero a un segmento, este es un error de tiempo de compilación ( especificación: expresiones de índice ).

Ejemplo:

pa := &[2]int{1, 2} fmt.Println(pa[1:1]) // OK fmt.Println(pa[1]) // OK ps := &[]int{3, 4} println(ps[1:1]) // Error: cannot slice ps (type *[]int) println(ps[1]) // Error: invalid operation: ps[1] (type *[]int does not support indexing)

8. Acceder a elementos de matriz (individuales) es más eficiente que acceder a elementos de segmento; Como en el caso de los segmentos, el tiempo de ejecución debe pasar por una desreferencia de puntero implícita. Además, "las expresiones len(s) y cap(s) son constantes si el tipo de s es una matriz o un puntero a una matriz" .

Puede ser sorprendente, pero incluso puedes escribir:

type IP [4]byte const x = len(IP{}) // x will be 4

Es válido y se evalúa en tiempo de compilación aunque IP{} no sea una expresión constante, por lo que, por ejemplo, const i = IP{} sería un error en tiempo de compilación. Después de esto, ni siquiera es sorprendente que lo siguiente también funcione:

const x2 = len((*IP)(nil)) // x2 will also be 4

Nota: cuando se extiende sobre una matriz completa frente a un segmento completo, puede que no haya ninguna diferencia de rendimiento, ya que obviamente se puede optimizar de modo que el puntero en el encabezado del segmento solo se desreferencia una vez. Para detalles / ejemplo, vea Array vs Slice: velocidad de acceso .

Consulte las preguntas relacionadas en las que se puede usar una matriz / tiene más sentido que una porción:

¿Por qué usar matrices en lugar de rebanadas?

¿Por qué no se puede utilizar Go slice como claves en los mapas de Go de la misma manera que las matrices se pueden utilizar como claves?

Hash con clave como tipo de matriz

Golang: ¿Cómo verifico la igualdad de tres valores con elegancia?

Cortar un puntero de corte pasado como argumento

Y esto es solo por curiosidad: una porción puede contenerse a sí misma mientras que una matriz no puede . (En realidad, esta propiedad facilita la comparación ya que no tiene que lidiar con estructuras de datos recursivas).

Blogs de lectura obligatoria:

Go Slices: uso y aspectos internos

Arreglos, cortes (y cadenas): la mecánica de ''agregar''


Las matrices son más eficientes para ahorrar espacio. Si nunca actualiza el tamaño del segmento (es decir, comienza con un tamaño predefinido y nunca lo supera), realmente no hay mucha diferencia de rendimiento. Pero hay una sobrecarga adicional en el espacio, ya que un segmento es simplemente un contenedor que contiene la matriz en su núcleo. Contextualmente, también mejora la claridad, ya que hace que el uso previsto de la variable sea más evidente.


Las matrices son valores, y a menudo es útil tener un valor en lugar de un puntero.

Los valores se pueden comparar, por lo tanto, puede usar matrices como claves de mapa.

Los valores siempre se inicializan, por lo que no es necesario inicializarlos o make como lo hace con un segmento.

Las matrices le dan un mejor control del diseño de la memoria, donde como no puede asignar espacio directamente en una estructura con un segmento, puede hacerlo con una matriz:

type Foo struct { buf [64]byte }

Aquí, un valor de Foo contendrá un valor de 64 bytes, en lugar de un encabezado de segmento que debe inicializarse por separado. Las matrices también se utilizan para rellenar estructuras para que coincidan con la alineación cuando interoperan con el código C y para evitar el intercambio falso para un mejor rendimiento de la caché.

Otro aspecto para mejorar el rendimiento es que puede definir mejor el diseño de la memoria que con los segmentos, porque la localidad de los datos puede tener un gran impacto en los cálculos intensivos de memoria. Anular la referencia de un puntero puede llevar un tiempo considerable en comparación con las operaciones que se realizan en los datos, y copiar valores más pequeños que una línea de caché conlleva un costo muy bajo, por lo que el código crítico de rendimiento a menudo usa matrices solo por esa razón.