Si j'ai une liste R mylist
, vous pouvez y ajouter un élément obj
comme ceci:
mylist[[length(mylist)+1]] <- obj
Mais il y a sûrement un moyen plus compact. Quand j'étais nouveau chez R, j'ai essayé d'écrire lappend()
comme ça:
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
mais bien sûr, cela ne fonctionne pas en raison de la sémantique de l'appel par nom de R ( lst
est effectivement copié lors de l'appel, donc les modifications apportées à lst
ne sont pas visibles en dehors de la portée de lappend()
. Je sais que vous pouvez effectuer un piratage d'environnement dans une fonction R pour atteindre en dehors de la portée de votre fonction et muter l'environnement d'appel, mais cela ressemble à un gros marteau pour écrire une simple fonction d'ajout.
Quelqu'un peut-il suggérer une meilleure façon de procéder? Points bonus si cela fonctionne pour les vecteurs et les listes.
Réponses:
S'il s'agit d'une liste de chaînes, utilisez simplement la
c()
fonction:Cela fonctionne aussi sur les vecteurs, donc j'obtiens les points bonus?
Edit (2015-Feb-01): Cet article arrive pour son cinquième anniversaire. Certains lecteurs aimables continuent de répéter toute lacune, donc voyez certainement certains des commentaires ci-dessous. Une suggestion pour les
list
types:En général, les types R peuvent rendre difficile d'avoir un et un seul idiome pour tous les types et utilisations.
la source
LL
aurait toujours deux éléments aprèsC(LL, c="harry")
est appelé.LL <- c(LL, c="harry")
.c()
a 2 arguments: la liste que j'essaie d'ajouter, à savoirlist(a=3, b=c(4, 5))
, et l'élément que j'essaie d'ajouter, à savoirc=c(6, 7)
. Si vous utilisez mon approche, vous verrez que 2 éléments de liste sont ajoutés (6
et7
, avec des nomsc1
etc2
) au lieu d'un seul vecteur à 2 éléments nomméc
comme cela est clairement prévu!mylist <- list(mylist, list(obj))
? Si oui, ce serait bien de modifier la réponseL'OP (dans la révision mise à jour de la question en avril 2012) souhaite savoir s'il existe un moyen d'ajouter à une liste en temps constant amorti, comme cela peut être fait, par exemple, avec un
vector<>
conteneur C ++ . Jusqu'à présent, la meilleure réponse (s?) Ne montre que les temps d'exécution relatifs pour diverses solutions étant donné un problème de taille fixe, mais ne traite pas directement de l' efficacité algorithmique des différentes solutions . Les commentaires ci-dessous contiennent de nombreuses réponses sur l'efficacité algorithmique de certaines des solutions, mais dans tous les cas à ce jour (en avril 2015), ils arrivent à une conclusion erronée.L'efficacité algorithmique capture les caractéristiques de croissance, soit en temps (temps d'exécution) soit en espace (quantité de mémoire consommée) à mesure que la taille d'un problème augmente . L'exécution d'un test de performances pour diverses solutions compte tenu d'un problème de taille fixe ne résout pas le taux de croissance des différentes solutions. L'OP souhaite savoir s'il existe un moyen d'ajouter des objets à une liste R en "temps constant amorti". Qu'est-ce que ça veut dire? Pour expliquer, permettez-moi d'abord de décrire le «temps constant»:
Croissance constante ou O (1) :
Si le temps nécessaire pour effectuer une tâche donnée reste le même que la taille du problème double , alors nous disons que l'algorithme présente un temps constant croissance , ou déclaré en notation "Big O", présente une croissance temporelle O (1). Lorsque l'OP dit temps constant "amorti", il signifie simplement "à long terme" ... c.-à-d., Si l'exécution d'une seule opération prend parfois beaucoup plus de temps que la normale (par exemple, si un tampon préalloué est épuisé et nécessite parfois un redimensionnement à un plus grand taille du tampon), tant que la performance moyenne à long terme est à temps constant, nous l'appellerons toujours O (1).
A titre de comparaison, je décrirai également le "temps linéaire" et le "temps quadratique":
Croissance linéaire ou O (n) :
Si le temps requis pour effectuer une tâche donnée double comme la taille du problème double , alors nous disons que l'algorithme présente un temps linéaire , ou une croissance O (n) .
Croissance quadratique ou O (n 2 ) :
Si le temps requis pour effectuer une tâche donnée augmente du carré de la taille du problème , nous disons que l'algorithme présente une croissance en temps quadratique , ou O (n 2 ) .
Il existe de nombreuses autres classes d'efficacité d'algorithmes; Je m'en remets à l'article Wikipédia pour une discussion plus approfondie.
Je remercie @CronAcronis pour sa réponse, car je suis nouveau à R et c'était agréable d'avoir un bloc de code entièrement construit pour faire une analyse des performances des différentes solutions présentées sur cette page. J'emprunte son code pour mon analyse, que je reproduis (enveloppé dans une fonction) ci-dessous:
Les résultats publiés par @CronAcronis semblent définitivement suggérer que la
a <- list(a, list(i))
méthode est la plus rapide, au moins pour une taille de problème de 10000, mais les résultats pour une seule taille de problème ne tiennent pas compte de la croissance de la solution. Pour cela, nous devons exécuter au moins deux tests de profilage, avec des tailles de problème différentes:Tout d'abord, un mot sur les valeurs min / lq / moyenne / médiane / uq / max: Puisque nous effectuons exactement la même tâche pour chacune des 5 exécutions, dans un monde idéal, nous pourrions nous attendre à ce que cela prenne exactement la même chose quantité de temps pour chaque course. Mais la première exécution est normalement biaisée vers des temps plus longs, car le code que nous testons n'est pas encore chargé dans le cache du CPU. Après la première exécution, nous nous attendons à ce que les temps soient assez cohérents, mais parfois notre code peut être expulsé du cache en raison d'interruptions de temporisation ou d'autres interruptions matérielles qui ne sont pas liées au code que nous testons. En testant les extraits de code 5 fois, nous permettons au code d'être chargé dans le cache lors de la première exécution, puis nous donnons à chaque extrait 4 chances de s'exécuter jusqu'à son terme sans interférence d'événements extérieurs. Pour cette raison,
Notez que j'ai choisi d'exécuter d'abord avec une taille de problème de 2000 puis 20000, donc ma taille de problème a augmenté d'un facteur 10 de la première exécution à la seconde.
Performance de la
list
solution: O (1) (temps constant)Examinons d'abord la croissance de la
list
solution, car nous pouvons dire tout de suite que c'est la solution la plus rapide dans les deux exécutions de profilage: lors de la première exécution, il a fallu 854 micro secondes (0,854 milli seconde) pour effectuer 2000 tâches d'ajout. Lors de la deuxième exécution, il a fallu 8,746 millisecondes pour effectuer 20000 tâches "d'ajout". Un observateur naïf dirait: «Ah, lalist
solution présente une croissance O (n), car à mesure que la taille du problème augmentait d'un facteur dix, le temps requis pour exécuter le test augmentait également. Le problème avec cette analyse est que ce que l'OP veut, c'est le taux de croissance d' une insertion d'objet unique , pas le taux de croissance du problème global. Sachant cela, il est clair quelist
fournit exactement ce que l'OP veut: une méthode pour ajouter des objets à une liste en temps O (1).Performance des autres solutions
Aucune des autres solutions ne se rapproche même de la vitesse de la
list
solution, mais il est tout de même instructif de les examiner:La plupart des autres solutions semblent être O (n) en termes de performances. Par exemple, la
by_index
solution, une solution très populaire basée sur la fréquence à laquelle je la trouve dans d'autres messages SO, a pris 11,6 millisecondes pour ajouter 2000 objets et 953 millisecondes pour ajouter dix fois plus d'objets. Le temps du problème global a augmenté d'un facteur 100, donc un observateur naïf pourrait dire «Ah, laby_index
solution présente une croissance O (n 2 ), car à mesure que la taille du problème augmentait d'un facteur dix, le temps nécessaire pour exécuter le test augmentait par un facteur de 100. "Comme précédemment, cette analyse est erronée, car l'OP s'intéresse à la croissance d'une insertion d'objet unique. Si nous divisons la croissance globale du temps par la croissance de la taille du problème, nous constatons que la croissance temporelle des objets ajoutés a augmenté d'un facteur de 10 seulement, et non d'un facteur de 100, ce qui correspond à la croissance de la taille du problème, donc laby_index
solution est O (n). Il n'y a pas de solutions répertoriées qui présentent une croissance O (n 2 ) pour ajouter un seul objet.la source
Dans les autres réponses, seule l'
list
approche aboutit à O (1), mais elle aboutit à une structure de liste profondément imbriquée, et non à une simple liste unique. J'ai utilisé les infrastructures de données ci-dessous, elles prennent en charge les ajouts O (1) (amortis) et permettent de convertir le résultat en une simple liste.et
Utilisez-les comme suit:
Ces solutions pourraient être développées en objets complets qui prennent en charge toutes les opérations liées aux listes, mais cela restera un exercice pour le lecteur.
Une autre variante pour une liste nommée:
Repères
Comparaison des performances en utilisant le code de @ phonetagger (qui est basé sur le code de @Cron Arconis). J'ai également ajouté un
better_env_as_container
et changéenv_as_container_
un peu. L'original aenv_as_container_
été cassé et ne stocke pas réellement tous les numéros.résultat:
J'ai ajouté
linkedList
etexpandingList
et une version en ligne des deux. IlinlinedLinkedList
s'agit essentiellement d'une copie delist_
, mais il convertit également la structure imbriquée en une liste simple. Au-delà, la différence entre les versions en ligne et non en ligne est due à la surcharge des appels de fonction.Toutes les variantes
expandingList
etlinkedList
affichent les performances de l'ajout O (1), le temps de référence évoluant linéairement avec le nombre d'éléments ajoutés.linkedList
est plus lent queexpandingList
, et la surcharge d'appel de fonction est également visible. Donc, si vous avez vraiment besoin de toute la vitesse que vous pouvez obtenir (et que vous souhaitez vous en tenir au code R), utilisez une version intégrée deexpandingList
.J'ai également jeté un œil à l'implémentation C de R, et les deux approches devraient être O (1) ajouter pour n'importe quelle taille jusqu'à ce que vous manquiez de mémoire.
J'ai également changé
env_as_container_
, la version originale stockerait chaque élément sous l'index "i", écrasant l'élément précédemment ajouté. Le quebetter_env_as_container
j'ai ajouté est très similaireenv_as_container_
mais sans lesdeparse
trucs. Les deux présentent des performances O (1), mais leur surcharge est un peu plus importante que les listes liées / développées.Surcharge mémoire
Dans l'implémentation CR, il y a une surcharge de 4 mots et 2 entiers par objet alloué. L'
linkedList
approche alloue une liste de longueur deux par ajout, pour un total de (4 * 8 + 4 + 4 + 2 * 8 =) 56 octets par élément ajouté sur les ordinateurs 64 bits (hors surcharge d'allocation de mémoire, donc probablement plus proche de 64 octets). L'expandingList
approche utilise un mot par élément ajouté, plus une copie lors du doublement de la longueur du vecteur, donc une utilisation totale de la mémoire allant jusqu'à 16 octets par élément. Étant donné que la mémoire se trouve dans un ou deux objets, la surcharge par objet est insignifiante. Je n'ai pas examiné en profondeur l'env
utilisation de la mémoire, mais je pense que ce sera plus prochelinkedList
.la source
list_
option est plus rapide et pourrait être utile si vous n'avez pas besoin de convertir en liste normale, c'est-à-dire si vous utilisez le résultat comme une pile.environment
ce que j'ai utilisé pour nestoR.) Mon goulot d'étranglement est presque toujours du temps humain passé à coder et à analyser des données, mais j'apprécie les points de repère que j'ai trouvés sur ce post. En ce qui concerne la surcharge de mémoire, cela ne me dérangerait pas jusqu'à environ un Ko par nœud pour mes applications. Je m'accroche à de grands tableaux, etc.Dans le Lisp, nous l'avons fait de cette façon:
bien que ce soit «contre», pas seulement «c». Si vous devez commencer avec une liste empy, utilisez l <- NULL.
la source
c()
fonction copie ses arguments dans un nouveau vecteur / liste et retourne cela.Vous voulez peut-être quelque chose comme ça?
Ce n'est pas une fonction très polie (assigner à
parent.frame()
est un peu grossier) mais IIUYC c'est ce que vous demandez.la source
J'ai fait une petite comparaison des méthodes mentionnées ici.
Résultats:
la source
list = list
n'était pas seulement le gagnant - mais de 1 à 2 commandes ou de magnitude!Si vous passez la variable de liste sous forme de chaîne entre guillemets, vous pouvez l'atteindre à partir de la fonction comme:
alors:
ou pour un crédit supplémentaire:
la source
Je ne sais pas pourquoi vous ne pensez pas que votre première méthode ne fonctionnera pas. Vous avez un bogue dans la fonction lappend: la longueur (liste) doit être la longueur (lst). Cela fonctionne bien et retourne une liste avec l'obj ajouté.
la source
lappend()
que j'ai fourni et il semble fonctionner aussi bien que c () et append (), qui présentent tous un comportement O (n ^ 2).essayez cette fonction lappend
et autres suggestions de cette page Ajouter un vecteur nommé à une liste
Au revoir.
la source
Je pense que ce que vous voulez faire est en fait passer par référence (pointeur) à la fonction - créer un nouvel environnement (qui est passé par référence aux fonctions) avec la liste qui lui est ajoutée:
Maintenant, vous ne faites que modifier la liste existante (pas en créer une nouvelle)
la source
Il s'agit d'un moyen simple d'ajouter des éléments à une liste R:
Ou par programmation:
la source
append()
fonction, mais c'est vraiment une fonction concaténée et elle ne fonctionne que sur les vecteurs.append()
fonctionne sur les vecteurs et les listes, et c'est un vrai append (qui est fondamentalement le même que concaténé, donc je ne vois pas quel est votre problème)en fait il y a une subtilité avec la
c()
fonction. Si tu fais:vous obtiendrez comme prévu:
mais si vous ajoutez une matrice avec
x <- c(x, matrix(5,2,2)
, votre liste aura encore 4 éléments de valeur5
! Vous feriez mieux de faire:Cela fonctionne pour tout autre objet et vous obtiendrez comme prévu:
Enfin, votre fonction devient:
et cela fonctionne pour tout type d'objet. Vous pouvez être plus intelligent et faire:
la source
Il y a aussi
list.append
durlist
( lien vers la documentation )C'est très simple et efficace.
la source
c()
oulist
. Les deux sont beaucoup plus rapides.rlist::list.append()
, c'est essentiellement un wrapper autourbase::c()
.Pour la validation, j'ai exécuté le code de référence fourni par @Cron. Il y a une différence majeure (en plus de fonctionner plus rapidement sur le nouveau processeur i7): le
by_index
maintenant fonctionne presque aussi bien que lelist_
:Pour référence, voici le code de référence copié textuellement de la réponse de @ Cron (juste au cas où il en modifierait le contenu plus tard):
la source
la source
C'est une question très intéressante et j'espère que ma réflexion ci-dessous pourrait y apporter une solution. Cette méthode donne une liste plate sans indexation, mais elle a list et unlist pour éviter les structures d'imbrication. Je ne suis pas sûr de la vitesse car je ne sais pas comment la comparer.
la source
mylist<-list(1,2,3) mylist<-c(mylist,list(5))
Nous pouvons donc facilement ajouter l'élément / objet en utilisant le code ci-dessus
la source