Se eu tiver alguma lista R mylist
, você pode anexar um item obj
a ele da seguinte forma:
mylist[[length(mylist)+1]] <- obj
Mas certamente há uma maneira mais compacta. Quando eu era novo na R, tentei escrever lappend()
assim:
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
mas é claro que isso não funciona devido à semântica de chamada por nome de R ( lst
é efetivamente copiada durante a chamada, portanto as alterações para lst
não são visíveis fora do escopo de lappend()
. Eu sei que você pode fazer hackers de ambiente em uma função R para alcançar fora do escopo de sua função e modifique o ambiente de chamada, mas isso parece um martelo grande para escrever uma função simples de acréscimo.
Alguém pode sugerir uma maneira mais bonita de fazer isso? Pontos de bônus se funcionar para vetores e listas.
Respostas:
Se for uma lista de cadeias, use a
c()
função:Isso também funciona em vetores, então recebo os pontos de bônus?
Edit (01/02/2015): Este post está chegando no seu quinto aniversário. Alguns leitores gentis continuam repetindo quaisquer deficiências com isso, então, por todos os meios, veja também alguns dos comentários abaixo. Uma sugestão para
list
tipos:Em geral, os tipos R podem dificultar a existência de um e apenas um idioma para todos os tipos e usos.
fonte
LL
ainda teria dois elementos depois deC(LL, c="harry")
ser chamado.LL <- c(LL, c="harry")
.c()
tem 2 argumentos: a lista que estou tentando anexar, a saberlist(a=3, b=c(4, 5))
, e o item que estou tentando anexar, a saberc=c(6, 7)
. Se você usar minha abordagem, verá que 2 itens da lista são anexados (6
e7
, com nomesc1
ec2
) em vez de um único vetor de 2 elementos nomeadoc
como pretendido claramente!mylist <- list(mylist, list(obj))
? Se sim, seria bom para modificar a respostaO OP (na revisão atualizada da pergunta de abril de 2012) está interessado em saber se há uma maneira de adicionar a uma lista no tempo constante amortizado, como pode ser feito, por exemplo, com um
vector<>
contêiner C ++ . As melhores respostas aqui até agora mostram apenas os tempos de execução relativos de várias soluções, devido a um problema de tamanho fixo, mas não abordam diretamente a eficiência algorítmica de várias soluções . Os comentários abaixo de muitas das respostas discutem a eficiência algorítmica de algumas das soluções, mas em todos os casos até o momento (em abril de 2015) eles chegaram à conclusão errada.A eficiência algorítmica captura as características de crescimento, no tempo (tempo de execução) ou no espaço (quantidade de memória consumida) à medida que o tamanho do problema aumenta . A execução de um teste de desempenho para várias soluções, devido a um problema de tamanho fixo, não aborda a taxa de crescimento das várias soluções. O OP está interessado em saber se existe uma maneira de anexar objetos a uma lista R em "tempo constante amortizado". O que isso significa? Para explicar, primeiro deixe-me descrever "tempo constante":
Crescimento constante ou O (1) :
Se o tempo necessário para executar uma determinada tarefa permanecer o mesmo que o tamanho do problema dobrar , dizemos que o algoritmo exibe crescimento de tempo constante , ou indicado na notação "Big O", exibe crescimento de tempo O (1). Quando o OP diz "tempo amortizado" constante, ele simplesmente significa "a longo prazo" ... ou seja, se executar uma única operação ocasionalmente leva muito mais tempo que o normal (por exemplo, se um buffer pré-alocado estiver esgotado e, ocasionalmente, for necessário redimensionar para um tamanho maior) tamanho do buffer), desde que o desempenho médio de longo prazo seja constante, ainda o chamaremos de O (1).
Para comparação, também descreverei "tempo linear" e "tempo quadrático":
Crescimento linear ou O (n) :
Se o tempo necessário para executar uma determinada tarefa dobrar à medida que o tamanho do problema dobrar , dizemos que o algoritmo exibe tempo linear ou crescimento de O (n) .
Crescimento quadrático ou O (n 2 ) :
Se o tempo necessário para executar uma determinada tarefa aumenta pelo quadrado do tamanho do problema , dizemos que o algoritmo exibe tempo quadrático ou crescimento de O (n 2 ) .
Existem muitas outras classes de eficiência de algoritmos; Adardo o artigo da Wikipedia para uma discussão mais aprofundada.
Agradeço ao @CronAcronis por sua resposta, pois sou novo no R e foi bom ter um bloco de código totalmente construído para fazer uma análise de desempenho das várias soluções apresentadas nesta página. Estou emprestando seu código para minha análise, que eu duplico (empacotado em uma função) abaixo:
Os resultados publicados por @CronAcronis definitivamente parecem sugerir que o
a <- list(a, list(i))
método é mais rápido, pelo menos para um tamanho de problema de 10000, mas os resultados para um único tamanho de problema não abordam o crescimento da solução. Para isso, precisamos executar no mínimo dois testes de criação de perfil, com diferentes tamanhos de problemas:Antes de mais nada, uma palavra sobre os valores min / lq / média / mediana / uq / max: Como estamos realizando exatamente a mesma tarefa para cada uma das 5 execuções, em um mundo ideal, poderíamos esperar que fosse necessário exatamente o mesmo quantidade de tempo para cada execução. Mas a primeira execução normalmente é influenciada por períodos mais longos, devido ao fato de que o código que estamos testando ainda não foi carregado no cache da CPU. Após a primeira execução, esperamos que os tempos sejam razoavelmente consistentes, mas, ocasionalmente, nosso código pode ser despejado do cache devido a interrupções de tick de timer ou outras interrupções de hardware que não estejam relacionadas ao código que estamos testando. Ao testar os trechos de código 5 vezes, estamos permitindo que o código seja carregado no cache durante a primeira execução e, em seguida, dando a cada trecho 4 chances de execução até a conclusão sem interferência de eventos externos. Por esta razão,
Observe que eu escolhi executar primeiro com um tamanho de problema de 2000 e, em seguida, 20000, portanto, o tamanho do meu problema aumentou em um fator de 10 da primeira execução para a segunda.
Desempenho da
list
solução: O (1) (tempo constante)Vamos primeiro examinar o crescimento da
list
solução, pois podemos dizer imediatamente que é a solução mais rápida nas duas execuções de criação de perfis: Na primeira execução, foram necessários 854 microssegundos (0,854 mili segundos) para executar 2000 tarefas " anexadas ". Na segunda execução, foram necessários 8.746 milissegundos para executar 20000 tarefas "anexar". Um observador ingênuo diria: "Ah, alist
solução exibe crescimento de O (n), pois, como o tamanho do problema aumentou em um fator de dez, o mesmo aconteceu com o tempo necessário para executar o teste". O problema com essa análise é que o que o OP deseja é a taxa de crescimento de uma única inserção de objeto , não a taxa de crescimento do problema geral. Sabendo disso, fica claro que olist
A solução fornece exatamente o que o OP deseja: um método de anexar objetos a uma lista em O (1).Desempenho das outras soluções
Nenhuma das outras soluções chega nem perto da velocidade da
list
solução, mas é informativo examiná-las de qualquer maneira:A maioria das outras soluções parece ter O (n) em desempenho. Por exemplo, a
by_index
solução, uma solução muito popular baseada na frequência com a qual eu a encontro em outras postagens de SO, levou 11,6 milissegundos para acrescentar 2000 objetos e 953 milissegundos para acrescentar dez vezes mais objetos. O tempo total do problema aumentou em um fator de 100; portanto, um observador ingênuo pode dizer "Ah, aby_index
solução exibe crescimento de O (n 2 ), pois como o tamanho do problema aumentou em um fator de dez, o tempo necessário para executar o teste aumentou. por um fator de 100 ".Como antes, essa análise é falha, uma vez que o OP está interessado no crescimento de uma inserção de um único objeto. Se dividirmos o crescimento do tempo geral pelo crescimento do tamanho do problema, descobrimos que o crescimento do tempo dos objetos anexos aumentou por um fator de apenas 10, e não um fator de 100, que corresponde ao crescimento do tamanho do problema; portanto, aby_index
solução é O (n) Não há soluções listadas que exibam crescimento de O (n 2 ) para anexar um único objeto.fonte
Nas outras respostas, apenas a
list
abordagem resulta em O (1) anexa, mas resulta em uma estrutura de lista profundamente aninhada, e não em uma lista simples e simples. Usei as estruturas de dados abaixo, elas suportam anexos O (1) (amortizados) e permitem que o resultado seja convertido novamente em uma lista simples.e
Use-os da seguinte maneira:
Essas soluções podem ser expandidas em objetos completos que suportam todas as operações relacionadas à lista, mas que permanecerão como um exercício para o leitor.
Outra variante para uma lista nomeada:
Benchmarks
Comparação de desempenho usando o código do @ phonetagger (que é baseado no código do @Cron Arconis). Eu também adicionei um
better_env_as_container
e mudeienv_as_container_
um pouco. O originalenv_as_container_
estava quebrado e, na verdade, não armazena todos os números.resultado:
Eu adicionei
linkedList
eexpandingList
e uma versão embutido de ambos. AinlinedLinkedList
é basicamente uma cópialist_
, mas também converte a estrutura de volta aninhada em uma lista simples. Além disso, a diferença entre as versões embutida e não embutida se deve à sobrecarga das chamadas de função.Todas as variantes de
expandingList
elinkedList
mostram O (1) acrescentam desempenho, com o tempo de referência escalando linearmente com o número de itens anexados.linkedList
é mais lento queexpandingList
e a sobrecarga da chamada de função também é visível. Portanto, se você realmente precisa de toda a velocidade possível (e deseja manter o código R), use uma versão embutida deexpandingList
.Também observei a implementação C do R, e as duas abordagens devem ser O (1) anexadas para qualquer tamanho até que você fique sem memória.
Também mudei
env_as_container_
, a versão original armazenaria todos os itens no índice "i", substituindo o item anexado anteriormente. O quebetter_env_as_container
eu adicionei é muito parecido,env_as_container_
mas sem asdeparse
coisas. Ambos exibem desempenho O (1), mas possuem uma sobrecarga um pouco maior que as listas vinculadas / expansíveis.Sobrecarga de memória
Na implementação do CR, há uma sobrecarga de 4 palavras e 2 ints por objeto alocado. A
linkedList
abordagem aloca uma lista de comprimento dois por anexo, para um total de (4 * 8 + 4 + 4 + 2 * 8 =) 56 bytes por item acrescentado em computadores de 64 bits (excluindo a sobrecarga de alocação de memória, provavelmente mais perto de 64 bytes). AexpandingList
abordagem usa uma palavra por item acrescentado, além de uma cópia ao dobrar o comprimento do vetor, para um uso total de memória de até 16 bytes por item. Como a memória está toda em um ou dois objetos, a sobrecarga por objeto é insignificante. Não examinei profundamente oenv
uso da memória, mas acho que será mais próximolinkedList
.fonte
list_
opção é mais rápida e pode ser útil se você não precisar converter para uma lista normal, ou seja, se você usar o resultado como uma pilha.environment
descuidado com as coisas que usei para o nestoR.) Meu gargalo é quase sempre o tempo humano gasto em codificação e análise de dados, mas aprecio os benchmarks que encontrei neste post. Quanto à sobrecarga de memória, eu não me importaria em cerca de um kB por nó para meus aplicativos. Eu mantenho grandes matrizes, etc.No Lisp, fizemos da seguinte maneira:
embora fosse 'contras', não apenas 'c'. Se você precisar começar com uma lista empy, use l <- NULL.
fonte
c()
função copia seus argumentos em um novo vetor / lista e retorna isso.Você quer algo assim talvez?
Não é uma função muito educada (atribuir a
parent.frame()
é meio rude), mas IIUYC é o que você está pedindo.fonte
Fiz uma pequena comparação dos métodos mencionados aqui.
Resultados:
fonte
list = list
não eram apenas os vencedores - mas por 1 a 2 ordens ou magnitude!Se você passar a variável da lista como uma string entre aspas, poderá alcançá-la na função, como:
tão:
ou para crédito extra:
fonte
Não sei por que você acha que seu primeiro método não funcionará. Você tem um erro na função lappend: length (list) deve ser length (lst). Isso funciona bem e retorna uma lista com o objeto anexado.
fonte
lappend()
que forneci e parece ter um desempenho tão bom quanto c () e append (), todos exibindo comportamento O (n ^ 2).tente esta função lappend
e outras sugestões desta página Adicione vetor nomeado a uma lista
Tchau.
fonte
Eu acho que o que você deseja fazer é realmente passar por referência (ponteiro) para a função - criar um novo ambiente (que é passado por referência a funções) com a lista adicionada a ele:
Agora você está apenas modificando a lista existente (não criando uma nova)
fonte
Esta é uma maneira simples de adicionar itens a uma Lista R:
Ou programaticamente:
fonte
append()
função, mas é realmente uma função concatenada e funciona apenas em vetores.append()
funciona em vetores e listas, e é um verdadeiro acréscimo (que é basicamente o mesmo que concatenate, então eu não vejo qual é seu problema)de fato, há um sub-subtipo com a
c()
função. Se você fizer:você obterá como esperado:
mas se você adicionar uma matriz com
x <- c(x, matrix(5,2,2)
, sua lista terá outros 4 elementos de valor5
! É melhor você fazer:Funciona para qualquer outro objeto e você obterá como esperado:
Finalmente, sua função se torna:
e funciona para qualquer tipo de objeto. Você pode ser mais inteligente e fazer:
fonte
Também existe
list.append
norlist
( link para a documentação )É muito simples e eficiente.
fonte
c()
oulist
-method. Ambos são muito mais rápidos.rlist::list.append()
, é essencialmente um invólucrobase::c()
.Para validação, executei o código de referência fornecido pelo @Cron. Há uma grande diferença (além de rodar mais rápido no processador i7 mais recente): o
by_index
desempenho agora é quase tão bom quanto olist_
:Para referência, aqui está o código de referência copiado literalmente da resposta de @ Cron (caso ele mude mais tarde o conteúdo):
fonte
fonte
Essa é uma pergunta muito interessante e espero que meu pensamento abaixo possa contribuir com uma solução. Esse método fornece uma lista simples sem indexação, mas possui list e unlist para evitar as estruturas de aninhamento. Não tenho certeza sobre a velocidade, pois não sei como compará-la.
fonte
mylist<-list(1,2,3) mylist<-c(mylist,list(5))
Assim, podemos acrescentar facilmente o elemento / objeto usando o código acima
fonte