Soy nuevo en Go y estoy muy emocionado por ello. Pero, en todos los lenguajes con los que he trabajado extensamente: Delphi, C #, C ++, Python: las listas son muy importantes porque se pueden cambiar de tamaño dinámicamente, a diferencia de las matrices.
En Golang, de hecho, hay una list.List
estructura, pero veo muy poca documentación al respecto, ya sea en Go By Example o en los tres libros de Go que tengo (Summerfield, Chisnal y Balbaert), todos pasan mucho tiempo en matrices y rebanadas y luego salte a mapas. En los ejemplos de código fuente también encuentro poco o ningún uso de list.List
.
También parece que, a diferencia de Python, Range
no es compatible con List, un gran inconveniente en mi opinión. ¿Me estoy perdiendo de algo?
Las rebanadas son ciertamente agradables, pero aún deben basarse en una matriz con un tamaño codificado de forma rígida. Ahí es donde entra List. ¿Hay alguna manera de crear una matriz / segmento en Go sin un tamaño de matriz codificado? ¿Por qué se ignora List?
list
tipo de Python no se implementa mediante una lista vinculada: se comporta de manera similar a un segmento de Go, y en ocasiones requiere que las copias de datos se expandan.std::list
casi siempre es una mala idea.std::vector
es lo que desea administrar una secuencia de elementos. Por las mismas razones,std::vector
se prefiere, también se prefiere el segmento Go.std::vector<T>
se incluyó en lalist
categoría porque no requiere un valor constante para la inicialización y se puede cambiar de tamaño dinámicamente. Cuando hice la pregunta, no me quedó claro que Go'sslice
pudiera usarse de manera similar; todo lo que leí en ese momento explicaba que un segmento era una "vista de una matriz" y, como en la mayoría de los otros idiomas, matrices simples de vainilla en Go deben declararse con un tamaño constante. (Pero gracias por el aviso.)Respuestas:
Casi siempre, cuando esté pensando en una lista, utilice una porción en Go. Las rebanadas se redimensionan dinámicamente. Debajo de ellos hay una porción de memoria contigua que puede cambiar de tamaño.
Son muy flexibles, como verá si lee la página wiki de SliceTricks .
Aquí hay un extracto:
Actualización : aquí hay un enlace a una publicación de blog sobre las secciones del propio equipo de Go, que explica muy bien la relación entre las secciones y las matrices y las partes internas de las secciones.
fuente
Hice esta pregunta hace unos meses, cuando comencé a investigar Go. Desde entonces, todos los días he estado leyendo sobre Go y codificando en Go.
Debido a que no recibí una respuesta clara a esta pregunta (aunque había aceptado una respuesta), ahora la responderé yo mismo, basándome en lo que he aprendido, desde que la hice:
Si. Las rebanadas no requieren una matriz codificada
slice
desde:var sl []int = make([]int,len,cap)
Este código asigna un segmento
sl
de tamañolen
con una capacidad decap
-len
ycap
son variables que se pueden asignar en tiempo de ejecución.Parece que las principales razones que
list.List
parecen recibir poca atención en Go son:Como se explicó en la respuesta de @Nick Craig-Wood, no hay prácticamente nada que se pueda hacer con listas que no se pueda hacer con porciones, a menudo de manera más eficiente y con una sintaxis más limpia y elegante. Por ejemplo, la construcción de rango:
for i:=range sl { sl[i]=i }
no se puede usar con la lista; se requiere un estilo C para el bucle. Y en muchos casos, la sintaxis de estilo de colección de C ++ debe usarse con listas:
push_back
etc.Quizás lo más importante
list.List
es que no está muy tipado, es muy similar a las listas y diccionarios de Python, que permiten mezclar varios tipos en la colección. Esto parece ir en contra del enfoque Go de las cosas. Go es un lenguaje muy tipado; por ejemplo, las conversiones de tipo implícitas nunca se permiten en Go, incluso un upCast deint
aint64
debe ser explícito. Pero todos los métodos para list.List toman interfaces vacías, todo vale.Una de las razones por las que abandoné Python y me mudé a Go es por este tipo de debilidad en el sistema de tipos de Python, aunque Python afirma estar "fuertemente tipado" (IMO no lo es). Go's
list.List
parece ser una especie de "mestizo", nacido de C ++vector<T>
y PythonList()
, y quizás esté un poco fuera de lugar en Go.No me sorprendería si en algún momento en un futuro no muy lejano, encontráramos list.List obsoleto en Go, aunque tal vez permanecerá, para dar cabida a esas situaciones raras donde, incluso utilizando buenas prácticas de diseño, un problema se puede resolver mejor con una colección que contiene varios tipos. O tal vez esté ahí para proporcionar un "puente" para que los desarrolladores de la familia C se sientan cómodos con Go antes de que aprendan los matices de los cortes, que son exclusivos de Go, AFAIK. (En algunos aspectos, los segmentos parecen similares a las clases de flujo en C ++ o Delphi, pero no del todo).
Aunque provengo de una experiencia en Delphi / C ++ / Python, en mi exposición inicial a Go encontré
list.List
que era más familiar que los segmentos de Go, ya que me he sentido más cómodo con Go, volví y cambié todas mis listas a segmentos. Todavía no he encontrado nada queslice
y / omap
no me permita hacer, por lo que necesito usarlist.List
.fuente
Creo que eso se debe a que no hay mucho que decir sobre ellos, ya que el
container/list
paquete se explica por sí mismo una vez que asimila cuál es el idioma principal de Go para trabajar con datos genéricos.En Delphi (sin genéricos) o en C, almacenaría punteros
TObject
os en la lista y luego los devolvería a sus tipos reales al obtenerlos de la lista. En C ++, las listas STL son plantillas y, por lo tanto, están parametrizadas por tipo, y en C # (estos días) las listas son genéricas.En Go,
container/list
almacena valores de tipointerface{}
que es un tipo especial capaz de representar valores de cualquier otro tipo (real), almacenando un par de punteros: uno a la información de tipo del valor contenido y un puntero al valor (o el valor directamente, si su tamaño no es mayor que el tamaño de un puntero). Entonces, cuando desee agregar un elemento a la lista, simplemente hágalo como parámetros de función de tipointerface{}
aceptar valores de cualquier tipo. Pero cuando extrae valores de la lista, y qué trabajar con sus tipos reales, debe marcarlos a máquina o hacer un cambio de tipo en ellos; ambos enfoques son solo formas diferentes de hacer esencialmente lo mismo.Aquí hay un ejemplo tomado de aquí :
package main import ("fmt" ; "container/list") func main() { var x list.List x.PushBack(1) x.PushBack(2) x.PushBack(3) for e := x.Front(); e != nil; e=e.Next() { fmt.Println(e.Value.(int)) } }
Aquí obtenemos el valor de un elemento usando
e.Value()
y luego lo afirmamos comoint
un tipo del valor insertado original.Puede leer sobre afirmaciones de tipo y cambios de tipo en "Effective Go" o en cualquier otro libro de introducción. La
container/list
documentación del paquete resume todos los métodos compatibles con las listas.fuente
range
es un lenguaje integrado que solo es aplicable a tipos integrados (matrices, segmentos, cadenas y mapas) porque cada "invocación" orange
de hecho producirá un código de máquina diferente para atravesar el contenedor es aplicado a.container/list
proporciona una lista de doble enlace . Esto significa que la indexación es unaO(N)
operación (tiene que comenzar por la cabeza y atravesar cada elemento hacia la cola, contando), y uno de los paradigmas de diseño de piedra angular de Go no tiene costos de rendimiento ocultos; y otro es que poner una pequeña carga adicional en el programador (implementar una función de indexación para una lista de doble enlace es una obviedad de 10 líneas) está bien. Entonces, el contenedor solo implementa operaciones "canónicas" sensibles a su tipo.TList
y otros de su tipo usan una matriz dinámica debajo, por lo que extender dicha lista no es barato mientras que indexarlo es barato. Entonces, mientras que las "listas" de Delphi parecen listas abstractas, de hecho son matrices, para lo que usarías porciones en Go. Lo que quiero resaltar es que Go se esfuerza por dejar las cosas claras sin acumular "hermosas abstracciones" y "ocultar" los detalles al programador. El enfoque de Go es más parecido al de C, donde usted sabe explícitamente cómo se distribuyen sus datos y cómo accede a ellos.Tenga en cuenta que los cortes de Go se pueden expandir mediante la
append()
función incorporada. Si bien esto a veces requerirá hacer una copia de la matriz de respaldo, no sucederá siempre, ya que Go sobredimensionará la nueva matriz, lo que le dará una capacidad mayor que la longitud informada. Esto significa que se puede completar una operación de adición posterior sin otra copia de datos.Si bien termina con más copias de datos que con el código equivalente implementado con listas vinculadas, elimina la necesidad de asignar elementos en la lista individualmente y la necesidad de actualizar los
Next
punteros. Para muchos usos, la implementación basada en matrices proporciona un rendimiento mejor o suficientemente bueno, así que eso es lo que se enfatiza en el lenguaje. Curiosamente, ellist
tipo estándar de Python también está respaldado por una matriz y tiene características de rendimiento similares al agregar valores.Dicho esto, hay casos en los que las listas enlazadas son una mejor opción (por ejemplo, cuando necesita insertar o eliminar elementos del principio / medio de una lista larga), y es por eso que se proporciona una implementación de biblioteca estándar. Supongo que no agregaron ninguna característica de lenguaje especial para trabajar con ellos porque estos casos son menos comunes que aquellos en los que se usan porciones.
fuente
append()
operación, como expliqué (que a veces implicará una copia de datos).A menos que el segmento se actualice con demasiada frecuencia (eliminar, agregar elementos en ubicaciones aleatorias), la contigüidad de los segmentos en la memoria ofrecerá una excelente proporción de aciertos de caché en comparación con las listas vinculadas.
Charla de Scott Meyer sobre la importancia del caché ... https://www.youtube.com/watch?v=WDIkqP4JbkE
fuente
list.List
se implementa como una lista doblemente enlazada. Las listas basadas en matrices (vectores en C ++ o porciones en golang) son una mejor opción que las listas enlazadas en la mayoría de las condiciones si no inserta con frecuencia en el medio de la lista. La complejidad del tiempo amortizado para añadir es O (1) tanto para la lista de matrices como para la lista vinculada, aunque la lista de matrices tiene que ampliar la capacidad y copiar los valores existentes. Las listas de matrices tienen un acceso aleatorio más rápido, una huella de memoria más pequeña y, lo que es más importante, son amigables para el recolector de basura porque no tienen punteros dentro de la estructura de datos.fuente
De: https://groups.google.com/forum/#!msg/golang-nuts/mPKCoYNwsoU/tLefhE7tQjMJ
Por lo tanto,
use el segmento
1. Si el orden de los elementos en la lista no es importante y necesita eliminarlo, simplemente
use Lista intercambiar el elemento para eliminar con el último elemento y volver a dividirlo en (longitud-1)
2. cuando los elementos sean más ( cualquier otro medio)
There are ways to mitigate the deletion problem -- e.g. the swap trick you mentioned or just marking the elements as logically deleted. But it's impossible to mitigate the problem of slowness of walking linked lists.
Por lo tanto,
use el segmento
1. Si necesita velocidad en el recorrido
fuente