Si tengo alguna lista R mylist
, puede agregarle un elemento obj
de esta manera:
mylist[[length(mylist)+1]] <- obj
Pero seguramente hay una forma más compacta. Cuando era nuevo en R, intenté escribir lappend()
así:
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
pero, por supuesto, eso no funciona debido a la semántica de llamada por nombre de R ( lst
se copia efectivamente en la llamada, por lo que los cambios lst
no son visibles fuera del alcance de lappend()
. Sé que puede hacer pirateo del entorno en una función de R para llegar fuera del alcance de su función y mutar el entorno de llamada, pero eso parece un gran martillo para escribir una función de adición simple.
¿Alguien puede sugerir una forma más hermosa de hacer esto? Puntos de bonificación si funciona tanto para vectores como para listas.
Respuestas:
Si es una lista de cadenas, simplemente use la
c()
función:Eso también funciona en vectores, así que ¿obtengo los puntos de bonificación?
Editar (2015-Feb-01): esta publicación se acerca en su quinto cumpleaños. Algunos lectores amables siguen repitiendo cualquier deficiencia con él, por lo que también deben ver algunos de los comentarios a continuación. Una sugerencia para los
list
tipos:En general, los tipos R pueden dificultar tener un solo idioma para todos los tipos y usos.
fuente
LL
aún tendría dos elementos después de queC(LL, c="harry")
se llame.LL <- c(LL, c="harry")
.c()
tiene 2 argumentos: la lista a la que estoy tratando de agregar, a saberlist(a=3, b=c(4, 5))
, y el elemento que estoy tratando de agregar, a saberc=c(6, 7)
. Si usa mi enfoque, verá que se añaden 2 elementos de la lista (6
y7
, con nombresc1
yc2
) en lugar de un solo vector de 2 elementos con el nombrec
claramente indicado.mylist <- list(mylist, list(obj))
? En caso afirmativo, sería bueno modificar la respuestaEl OP (en la revisión actualizada de la pregunta de abril de 2012) está interesado en saber si hay una manera de agregar a una lista en tiempo constante amortizado, como se puede hacer, por ejemplo, con un
vector<>
contenedor C ++ . La mejor respuesta (s) aquí hasta ahora solo muestra los tiempos de ejecución relativos para varias soluciones dado un problema de tamaño fijo, pero no aborda directamente la eficiencia algorítmica de ninguna de las soluciones . Los comentarios debajo de muchas de las respuestas discuten la eficiencia algorítmica de algunas de las soluciones, pero en todos los casos hasta la fecha (a partir de abril de 2015) llegan a una conclusión incorrecta.La eficiencia algorítmica captura las características de crecimiento, ya sea en tiempo (tiempo de ejecución) o espacio (cantidad de memoria consumida) a medida que crece el tamaño del problema . La ejecución de una prueba de rendimiento para varias soluciones dado un problema de tamaño fijo no aborda la tasa de crecimiento de las diferentes soluciones. El OP está interesado en saber si hay una manera de agregar objetos a una lista R en "tiempo constante amortizado". Qué significa eso? Para explicar, primero déjame describir el "tiempo constante":
Crecimiento constante u O (1) :
Si el tiempo requerido para realizar una tarea determinada sigue siendo el mismo que el tamaño del problema se duplica , entonces decimos que el algoritmo exhibe un crecimiento de tiempo constante , o establecido en la notación "Big O", exhibe un crecimiento de tiempo O (1). Cuando el OP dice tiempo constante "amortizado", simplemente quiere decir "a largo plazo" ... es decir, si realizar una sola operación ocasionalmente lleva mucho más tiempo de lo normal (por ejemplo, si un búfer preasignado se agota y ocasionalmente requiere cambiar el tamaño a un tamaño mayor tamaño del búfer), siempre que el rendimiento promedio a largo plazo sea un tiempo constante, todavía lo llamaremos O (1).
A modo de comparación, también describiré "tiempo lineal" y "tiempo cuadrático":
Crecimiento lineal u O (n) :
Si el tiempo requerido para realizar una tarea dada dobles como el tamaño de los problemas dobles , entonces decimos las exposiciones algoritmo lineal de tiempo , o O (n) el crecimiento.
Crecimiento cuadrático u O (n 2 ) :
Si el tiempo requerido para realizar una tarea determinada aumenta al cuadrado del tamaño del problema , les decimos que el algoritmo exhibe un tiempo cuadrático o un crecimiento de O (n 2 ) .
Hay muchas otras clases de algoritmos de eficiencia; Me remito al artículo de Wikipedia para mayor discusión.
Agradezco a @CronAcronis por su respuesta, ya que soy nuevo en R y fue bueno tener un bloque de código completamente construido para hacer un análisis de rendimiento de las diversas soluciones presentadas en esta página. Estoy tomando prestado su código para mi análisis, que duplico (envuelto en una función) a continuación:
Los resultados publicados por @CronAcronis definitivamente parecen sugerir que el
a <- list(a, list(i))
método es más rápido, al menos para un tamaño de problema de 10000, pero los resultados para un tamaño de problema único no abordan el crecimiento de la solución. Para eso, necesitamos ejecutar un mínimo de dos pruebas de perfil, con diferentes tamaños de problemas:En primer lugar, una palabra sobre los valores min / lq / mean / median / uq / max: dado que estamos realizando exactamente la misma tarea para cada una de las 5 ejecuciones, en un mundo ideal, podríamos esperar que tomaría exactamente lo mismo cantidad de tiempo para cada carrera. Pero la primera ejecución normalmente está sesgada hacia tiempos más largos debido al hecho de que el código que estamos probando aún no está cargado en la memoria caché de la CPU. Después de la primera ejecución, esperaríamos que los tiempos sean bastante consistentes, pero ocasionalmente nuestro código puede ser desalojado de la caché debido a interrupciones de temporizador u otras interrupciones de hardware que no están relacionadas con el código que estamos probando. Al probar los fragmentos de código 5 veces, permitimos que el código se cargue en el caché durante la primera ejecución y luego le damos a cada fragmento 4 posibilidades de ejecutarse hasta su finalización sin interferencia de eventos externos. Por esta razón,
Tenga en cuenta que elegí ejecutar primero con un tamaño de problema de 2000 y luego de 20000, por lo que el tamaño de mi problema aumentó en un factor de 10 de la primera ejecución a la segunda.
Rendimiento de la
list
solución: O (1) (tiempo constante)Primero veamos el crecimiento de la
list
solución, ya que podemos decir de inmediato que es la solución más rápida en ambas ejecuciones de creación de perfiles: en la primera ejecución, se necesitaron 854 microsegundos (0.854 milisegundos ) para realizar 2000 tareas de "agregar". En la segunda ejecución, se necesitaron 8.746 milisegundos para realizar 20000 tareas de "agregar". Un observador ingenuo diría: "Ah, lalist
solución muestra un crecimiento de O (n), ya que a medida que el tamaño del problema creció en un factor de diez, también lo hizo el tiempo requerido para ejecutar la prueba". El problema con ese análisis es que lo que quiere el OP es la tasa de crecimiento de la inserción de un solo objeto , no la tasa de crecimiento del problema general. Sabiendo eso, está claro que ellist
La solución proporciona exactamente lo que quiere el OP: un método para agregar objetos a una lista en O (1) tiempo.Rendimiento de las otras soluciones.
Ninguna de las otras soluciones se acerca a la velocidad de la
list
solución, pero es informativo examinarlas de todos modos:La mayoría de las otras soluciones parecen ser O (n) en rendimiento. Por ejemplo, la
by_index
solución, una solución muy popular basada en la frecuencia con la que la encuentro en otras publicaciones de SO, tardó 11,6 milisegundos en agregar 2000 objetos y 953 milisegundos en agregar diez veces esa cantidad de objetos. El tiempo general del problema aumentó en un factor de 100, por lo que un observador ingenuo podría decir "Ah, laby_index
solución muestra un crecimiento de O (n 2 ), ya que a medida que el tamaño del problema aumentó en un factor de diez, el tiempo requerido para ejecutar la prueba aumentó por un factor de 100 ".Como antes, este análisis es defectuoso, ya que el OP está interesado en el crecimiento de una inserción de un solo objeto. Si dividimos el crecimiento del tiempo general por el crecimiento del tamaño del problema, encontramos que el crecimiento del tiempo de los objetos anexos aumentó en un factor de solo 10, no un factor de 100, que coincide con el crecimiento del tamaño del problema, por lo que laby_index
solución es O (norte). No hay soluciones enumeradas que exhiban un crecimiento de O (n 2 ) para agregar un solo objeto.fuente
En las otras respuestas, solo el
list
enfoque da como resultado O (1), pero da como resultado una estructura de lista profundamente anidada, y no una lista simple. He utilizado las siguientes estructuras de datos, admiten los anexos O (1) (amortizados) y permiten que el resultado se convierta de nuevo en una lista simple.y
Úselos de la siguiente manera:
Estas soluciones podrían expandirse en objetos completos que admitan todas las operaciones relacionadas con la lista, pero eso seguirá siendo un ejercicio para el lector.
Otra variante para una lista con nombre:
Puntos de referencia
Comparación de rendimiento utilizando el código de @ phonetagger (que se basa en el código de @Cron Arconis). También agregué un
better_env_as_container
y cambiéenv_as_container_
un poco. El originalenv_as_container_
estaba roto y en realidad no almacena todos los números.resultado:
He agregado
linkedList
yexpandingList
una versión en línea de ambos. ElinlinedLinkedList
es básicamente una copialist_
, sino que también convierte la parte de atrás estructura anidada en una lista simple. Más allá de eso, la diferencia entre las versiones en línea y no en línea se debe a la sobrecarga de las llamadas a funciones.Todas las variantes de
expandingList
ylinkedList
muestran O (1) agregan rendimiento, con el tiempo de referencia escalando linealmente con el número de elementos agregados.linkedList
es más lento queexpandingList
, y la sobrecarga de la llamada a la función también es visible. Entonces, si realmente necesita toda la velocidad que puede obtener (y desea apegarse al código R), use una versión en línea deexpandingList
.También he echado un vistazo a la implementación C de R, y ambos enfoques deben ser O (1) agregar para cualquier tamaño hasta que se quede sin memoria.
También he cambiado
env_as_container_
, la versión original almacenaría cada elemento en el índice "i", sobrescribiendo el elemento adjunto anteriormente. Elbetter_env_as_container
que he agregado es muy similarenv_as_container_
pero sin lasdeparse
cosas. Ambos exhiben un rendimiento O (1), pero tienen una sobrecarga que es bastante mayor que las listas vinculadas / expandidas.Sobrecarga de memoria
En la implementación de CR hay una sobrecarga de 4 palabras y 2 entradas por objeto asignado. El
linkedList
enfoque asigna una lista de longitud dos por apéndice, para un total de (4 * 8 + 4 + 4 + 2 * 8 =) 56 bytes por elemento agregado en computadoras de 64 bits (excluyendo la sobrecarga de asignación de memoria, por lo que probablemente esté más cerca de 64 bytes). ElexpandingList
enfoque utiliza una palabra por elemento adjunto, más una copia al duplicar la longitud del vector, por lo que un uso de memoria total de hasta 16 bytes por elemento. Como la memoria está en uno o dos objetos, la sobrecarga por objeto es insignificante. No he examinado profundamente elenv
uso de la memoria, pero creo que estará más cercalinkedList
.fuente
list_
opción es más rápida y podría ser útil si no necesita convertir a una lista normal, es decir, si utiliza el resultado como una pila.environment
cosas que utilicé para nestoR). Mi cuello de botella casi siempre es tiempo humano dedicado a codificar y hacer análisis de datos, pero aprecio los puntos de referencia que he encontrado en esta publicación. En cuanto a la sobrecarga de memoria, no me importaría hasta aproximadamente un KB por nodo para mis aplicaciones. Me aferro a grandes matrices, etc.En el Lisp lo hicimos de esta manera:
aunque era 'contras', no solo 'c'. Si necesita comenzar con una lista vacía, use l <- NULL.
fuente
c()
función copia sus argumentos en un nuevo vector / lista y lo devuelve.¿Quieres algo como esto quizás?
No es una función muy educada (asignar
parent.frame()
es un poco grosero) pero IIUYC es lo que estás pidiendo.fuente
He hecho una pequeña comparación de los métodos mencionados aquí.
Resultados:
fuente
list = list
no solo fueron el ganador, ¡sino de 1 a 2 órdenes o magnitud!Si pasa la variable de lista como una cadena entre comillas, puede alcanzarla desde la función como:
entonces:
o por crédito extra:
fuente
No estoy seguro de por qué no crees que tu primer método no funcionará. Tiene un error en la función lappend: longitud (lista) debe ser longitud (lst). Esto funciona bien y devuelve una lista con el obj adjunto.
fuente
lappend()
que he proporcionado y parece funcionar tan bien como c () y append (), todos los cuales exhiben un comportamiento O (n ^ 2).prueba esta función lappend
y otras sugerencias de esta página Agregar un vector con nombre a una lista
Adiós.
fuente
Creo que lo que quiere hacer es en realidad pasan por referencia (puntero) a la function-- crear un nuevo entorno (que se pasa por referencia a las funciones) con la lista añadido a la misma:
Ahora solo está modificando la lista existente (sin crear una nueva)
fuente
Esta es una forma sencilla de agregar elementos a una Lista R:
O programáticamente:
fuente
append()
función, pero en realidad es una función concatenada y solo funciona en vectores.append()
trabaja en vectores y listas, y es un verdadero modo de adición (que es básicamente el mismo que concatenar, así que no veo cuál es tu problema)De hecho, hay una subtelty con la
c()
función. Si lo haces:obtendrá lo esperado:
¡pero si agrega una matriz con
x <- c(x, matrix(5,2,2)
, su lista tendrá otros 4 elementos de valor5
! Será mejor que hagas:Funciona para cualquier otro objeto y obtendrá lo esperado:
Finalmente, tu función se convierte en:
y funciona para cualquier tipo de objeto. Puedes ser más inteligente y hacer:
fuente
También hay
list.append
derlist
( enlace a la documentación )Es muy simple y eficiente.
fuente
c()
olist
. Ambos son mucho más rápidos.rlist::list.append()
, es esencialmente una envolturabase::c()
.Para la validación ejecuté el código de referencia proporcionado por @Cron. Hay una diferencia importante (además de correr más rápido en el nuevo procesador i7):
by_index
ahora funciona casi tan bien comolist_
:Como referencia, aquí está el código de referencia copiado literalmente de la respuesta de @ Cron (en caso de que luego cambie el contenido):
fuente
fuente
Esta es una pregunta muy interesante y espero que mi pensamiento a continuación pueda contribuir a solucionarlo. Este método proporciona una lista plana sin indexar, pero tiene una lista y una lista para evitar las estructuras de anidación. No estoy seguro de la velocidad ya que no sé cómo compararla.
fuente
mylist<-list(1,2,3) mylist<-c(mylist,list(5))
Entonces podemos agregar fácilmente el elemento / objeto usando el código anterior
fuente