Jeśli mam listę R mylist
, możesz obj
do niej dodać element w następujący sposób:
mylist[[length(mylist)+1]] <- obj
Ale na pewno jest jakiś bardziej zwarty sposób. Kiedy byłem nowy w R, próbowałem pisać w ten lappend()
sposób:
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
ale oczywiście to nie działa z powodu semantyki wywołania R według nazwy ( lst
jest skutecznie kopiowane podczas wywołania, więc zmiany lst
nie są widoczne poza zakresem lappend()
. Wiem, że możesz zrobić hakowanie środowiska za pomocą funkcji R, aby dotrzeć poza zakres funkcji i mutowanie środowiska wywołującego, ale wydaje się, że to duży młot do napisania prostej funkcji dołączania.
Czy ktoś może zasugerować piękniejszy sposób na zrobienie tego? Punkty bonusowe, jeśli działa zarówno dla wektorów, jak i list.
Odpowiedzi:
Jeśli jest to lista ciągów znaków, po prostu użyj
c()
funkcji:Działa to również na wektory, więc czy otrzymam punkty bonusowe?
Edycja (2015-luty-01): Ten wpis będzie miał piąte urodziny. Niektórzy życzliwi czytelnicy powtarzają wszelkie niedociągnięcia, więc zdecydowanie zobacz także niektóre z poniższych komentarzy. Jedna sugestia dla
list
typów:Ogólnie rzecz biorąc, typy R mogą utrudnić posiadanie jednego i tylko jednego idiomu dla wszystkich typów i zastosowań.
źródło
LL
nadal miałby dwa elementy poC(LL, c="harry")
wywołaniu.LL <- c(LL, c="harry")
.c()
ma 2 argumenty: listę, do której próbuję dołączyć, a mianowicielist(a=3, b=c(4, 5))
element, który próbuję dołączyć, mianowiciec=c(6, 7)
. Jeśli użyjesz mojego podejścia, zobaczysz, że 2 elementy listy są dołączane (6
wraz7
z nazwamic1
ic2
) zamiast jednego 2-elementowego wektora o nazwiec
tak, jak jest to wyraźnie zamierzone!mylist <- list(mylist, list(obj))
? Jeśli tak, dobrze byłoby zmodyfikować odpowiedźOP (w zaktualizowanej wersji pytania z kwietnia 2012 r.) Jest zainteresowany wiedzą, czy istnieje sposób, aby dodać do listy w zamortyzowanym stałym czasie, na przykład za pomocą
vector<>
kontenera C ++ . Najlepsza odpowiedź (odpowiedzi)? Jak dotąd pokazuje tylko względne czasy wykonania dla różnych rozwiązań, biorąc pod uwagę problem o stałej wielkości, ale nie odnosi się bezpośrednio do wydajności algorytmicznej różnych rozwiązań . Komentarze poniżej wielu odpowiedzi omawiają wydajność algorytmiczną niektórych rozwiązań, ale w każdym przypadku do tej pory (od kwietnia 2015 r.) Dochodzą do błędnych wniosków.Wydajność algorytmiczna rejestruje charakterystykę wzrostu, zarówno w czasie (czas wykonania), jak i przestrzeni (ilość zajętej pamięci) wraz ze wzrostem wielkości problemu . Przeprowadzenie testu wydajności dla różnych rozwiązań ze względu na problem o stałej wielkości nie odnosi się do tempa wzrostu różnych rozwiązań. OP chce wiedzieć, czy istnieje sposób na dodanie obiektów do listy R w „zamortyzowanym czasie stałym”. Co to znaczy? Aby wyjaśnić, najpierw pozwól mi opisać „stały czas”:
Stały wzrost lub O (1) :
Jeśli czas wymagany do wykonania danego zadania pozostaje taki sam, ponieważ rozmiar problemu podwaja się , to mówimy, że algorytm wykazuje stały wzrost czasu , lub określony w notacji „Big O”, wzrost czasu O (1). Kiedy OP mówi „zamortyzowany” stały czas, to po prostu znaczy „w długim okresie” ... tj. Jeśli wykonanie pojedynczej operacji czasami zajmuje znacznie więcej czasu niż normalnie (np. Jeśli wstępnie przydzielony bufor jest wyczerpany i czasami wymaga zmiany rozmiaru na większy rozmiar bufora), o ile średnia długoterminowa wydajność jest stała, nadal będziemy ją nazywać O (1).
Dla porównania opiszę również „czas liniowy” i „czas kwadratowy”:
Wzrost liniowy lub O (n) :
Jeśli czas potrzebny do wykonania określonego zadania podwójne jako wielkości problemu podwójnej , to znaczy z wystawy algorytmu liniowej czasie lub O (n) wzrostu.
Wzrost kwadratowy lub O (n 2 ) :
Jeśli czas wymagany do wykonania danego zadania zwiększa się o kwadrat wielkości problemu , to mówimy, że algorytm wykazuje kwadratowy czas lub wzrost O (n 2 ) .
Istnieje wiele innych klas wydajności algorytmów; Odsyłam do artykułu z Wikipedii w celu dalszej dyskusji.
Dziękuję @CronAcronis za jego odpowiedź, ponieważ jestem nowy w R i miło było mieć w pełni skonstruowany blok kodu do analizy wydajności różnych rozwiązań przedstawionych na tej stronie. Pożyczam jego kod do mojej analizy, którą powielam (zawinięty w funkcję) poniżej:
Wyniki opublikowane przez @CronAcronis zdecydowanie wydają się sugerować, że
a <- list(a, list(i))
metoda jest najszybsza, przynajmniej dla rozmiaru problemu 10000, ale wyniki dla pojedynczego rozmiaru problemu nie uwzględniają wzrostu rozwiązania. W tym celu musimy przeprowadzić co najmniej dwa testy profilowania o różnych rozmiarach problemów:Po pierwsze, słowo o wartościach min / lq / średnia / mediana / uq / max: Ponieważ wykonujemy dokładnie to samo zadanie dla każdego z 5 przebiegów, w idealnym świecie możemy oczekiwać, że zajmie to dokładnie to samo ilość czasu na każdy bieg. Jednak pierwsze uruchomienie jest zwykle tendencyjne w kierunku dłuższych czasów, ponieważ testowany kod nie jest jeszcze ładowany do pamięci podręcznej procesora. Po pierwszym uruchomieniu spodziewalibyśmy się, że czasy będą dość spójne, ale czasami nasz kod może zostać usunięty z pamięci podręcznej z powodu przerwania taktowania zegara lub innych przerwań sprzętowych niezwiązanych z testowanym kodem. Testując fragmenty kodu 5 razy, pozwalamy na załadowanie kodu do pamięci podręcznej podczas pierwszego uruchomienia, a następnie dajemy każdemu z 4 fragmentów szansę na ukończenie bez zakłócania przez zdarzenia zewnętrzne. Z tego powodu,
Zauważ, że wybrałem najpierw uruchomienie z problemem o wielkości 2000, a następnie 20000, więc mój rozmiar problemu wzrósł o współczynnik 10 od pierwszego uruchomienia do drugiego.
Wydajność
list
rozwiązania: O (1) (czas stały)Spójrzmy najpierw na rozwój
list
rozwiązania, ponieważ od razu możemy stwierdzić, że jest to najszybsze rozwiązanie w obu przebiegach profilowania: w pierwszym uruchomieniu wykonanie 2000 zadań „dopisania” zajęło 854 mikrosekund (0,854 milisekund ). W drugim uruchomieniu wykonanie 20000 zadań „dopisujących” zajęło 8,746 milisekund. Naiwny obserwator powiedziałby: „Ach,list
rozwiązanie wykazuje wzrost O (n), ponieważ wraz ze wzrostem wielkości problemu dziesięciokrotnie zwiększył się czas potrzebny na wykonanie testu”. Problem z tą analizą polega na tym, że PO chce szybkości wzrostu wstawienia pojedynczego obiektu , a nie szybkości wzrostu ogólnego problemu. Wiedząc o tym, jasne jest, żelist
rozwiązanie zapewnia dokładnie to, czego chce OP: metodę dołączania obiektów do listy w czasie O (1).Wydajność innych rozwiązań
Żadne z pozostałych rozwiązań nie zbliża się nawet do szybkości
list
rozwiązania, ale i tak warto je zbadać:Wydaje się, że większość innych rozwiązań ma wydajność O (n). Na przykład
by_index
rozwiązanie, bardzo popularne rozwiązanie oparte na częstotliwości, z jaką znajduję je w innych postach SO, zajęło 11,6 milisekund, aby dołączyć 2000 obiektów, i 953 milisekund, aby dołączyć dziesięć razy tyle obiektów. Czas ogólnego problemu wzrósł 100-krotnie, więc naiwny obserwator mógłby powiedzieć: „Ach,by_index
rozwiązanie wykazuje wzrost O (n 2 ), ponieważ wraz ze wzrostem wielkości problemu dziesięciokrotnie zwiększył się czas potrzebny na wykonanie testu 100 razy. ”Tak jak poprzednio, analiza ta jest błędna, ponieważ PO jest zainteresowany wzrostem wstawiania jednego obiektu. Jeśli podzielimy całkowity wzrost czasu przez wzrost wielkości problemu, okaże się, że wzrost czasu dołączanych obiektów zwiększył się tylko 10, a nie 100, co odpowiada wzrostowi wielkości problemu, więcby_index
rozwiązaniem jest O (n). Nie ma na liście rozwiązań, które wykazują wzrost O (n 2 ) przy dołączaniu pojedynczego obiektu.źródło
W pozostałych odpowiedziach tylko
list
podejście podejścia w O (1) dołącza, ale skutkuje głęboko zagnieżdżoną strukturą listy, a nie zwykłą pojedynczą listą. Użyłem poniższych struktur danych, obsługują one załączniki O (1) (amortyzowane) i pozwalają na przekonwertowanie wyniku z powrotem na zwykłą listę.i
Użyj ich w następujący sposób:
Rozwiązania te można rozszerzyć na pełne obiekty, które same obsługują wszystkie operacje związane z listami, ale pozostaną dla czytelnika ćwiczeniem.
Kolejny wariant dla nazwanej listy:
Benchmarki
Porównanie wydajności przy użyciu kodu @ phonetagger (który jest oparty na kodzie @Cron Arconis). Dodałem również
better_env_as_container
ienv_as_container_
nieco zmieniłem . Oryginałenv_as_container_
został zepsuty i nie przechowuje wszystkich numerów.wynik:
Dodałem
linkedList
iexpandingList
wbudowaną wersję obu. JestinlinedLinkedList
to w zasadzie kopialist_
, ale przekształca również zagnieżdżoną strukturę z powrotem w zwykłą listę. Poza tym różnica między wersjami wbudowanymi i nie wstawionymi wynika z narzutu wywołań funkcji.Wszystkie warianty
expandingList
ilinkedList
pokazują wydajność O (1), z skalowaniem czasu testu porównawczego liniowo z liczbą dołączanych elementów.linkedList
jest wolniejszy niżexpandingList
, a narzut funkcji jest również widoczny. Więc jeśli naprawdę potrzebujesz całej prędkości, jaką możesz uzyskać (i chcesz trzymać się kodu R), użyj wbudowanej wersjiexpandingList
.Spojrzałem również na implementację C w R i oba podejścia powinny być dołączone do O (1) dla dowolnego rozmiaru aż do wyczerpania pamięci.
Zmieniłem również
env_as_container_
, oryginalna wersja przechowywałaby każdy element pod indeksem „i”, zastępując wcześniej dołączony element.better_env_as_container
Dodałem jest bardzo podobny doenv_as_container_
, ale bezdeparse
rzeczy. Oba wykazują wydajność O (1), ale mają narzut, który jest nieco większy niż listy połączone / rozwijane.Narzut pamięci
W implementacji CR występuje narzut 4 słów i 2 ints na przydzielony obiekt.
linkedList
Przydziela podejście jedna lista dwóch długości za dodać, w sumie (4 * 8 + 4 + 4 + 2 * 8 =) 56 bajty na załączonym pozycji na komputerach 64-bitowych (bez alokacji pamięci górnym, więc prawdopodobnie bliżej 64 bajty).expandingList
Podejście wykorzystuje jedno słowo za załączonym pozycji, a także kopię gdy podwojenie długości wektora, więc całkowite zużycie pamięci do 16 bajtów na pozycji. Ponieważ pamięć jest w jednym lub dwóch obiektach, narzut na obiekt jest nieznaczny. Nie zagłębiłem się wenv
użycie pamięci, ale myślę, że będzie bliżejlinkedList
.źródło
list_
opcja jest szybsza i może być użyteczna, jeśli nie musisz konwertować na normalną listę, tj. Jeśli użyjesz wyniku jako stosu.environment
sprawach, których użyłem do nestoR.) Moim wąskim gardłem jest prawie zawsze ludzki czas spędzany na kodowaniu i analizie danych, ale doceniam testy porównawcze, które znalazłem w tym poście. Jeśli chodzi o narzut pamięci, nie miałbym nic przeciwko około kB na węzeł dla moich aplikacji. Trzymam się dużych tablic itp.W Lisp zrobiliśmy to w ten sposób:
chociaż były to „minusy”, a nie tylko „c”. Jeśli chcesz zacząć od listy empy, użyj l <- NULL.
źródło
c()
kopiuje swoje argumenty do nowego wektora / listy i zwraca.Może chcesz coś takiego?
Nie jest to bardzo uprzejma funkcja (przypisywanie do niej
parent.frame()
jest niegrzeczne), ale IIUYC jest tym, o co prosisz.źródło
Dokonałem niewielkiego porównania wymienionych tu metod.
Wyniki:
źródło
list = list
to nie tylko zwycięzca - ale o 1 do 2 zamówień lub wielkości!Jeśli przekażesz zmienną listy jako ciąg cytowany, możesz uzyskać do niej dostęp z funkcji takiej jak:
więc:
lub dla dodatkowego kredytu:
źródło
Nie jestem pewien, dlaczego uważasz, że Twoja pierwsza metoda nie zadziała. Masz błąd w funkcji lappend: długość (lista) powinna być długością (pierwsza). Działa to dobrze i zwraca listę z dołączonym obj.
źródło
lappend()
dostarczone przeze mnie i wydaje się, że działają one równie dobrze jak c () i append (), z których wszystkie wykazują zachowanie O (n ^ 2).wypróbuj tę funkcję lappend
i inne sugestie z tej strony Dodaj nazwany wektor do listy
PA.
źródło
Myślę, że to, co chcesz zrobić, to faktycznie przechodzą przez odniesienie (wskaźnik) do function-- stworzyć nowe środowisko, (które są przekazywane przez referencję do funkcji) z listy dodano do niego:
Teraz modyfikujesz tylko istniejącą listę (nie tworzysz nowej)
źródło
Jest to prosty sposób dodawania elementów do listy R:
Lub programowo:
źródło
append()
funkcję, ale tak naprawdę jest to funkcja łącząca i działa tylko na wektorach.append()
działa na wektorach i listach i jest to prawdziwy dodatek (który jest w zasadzie taki sam jak konkatenacja, więc nie rozumiem, na czym polega twój problem)w rzeczywistości istnieje subtelność z
c()
funkcją. Jeśli zrobisz:otrzymasz zgodnie z oczekiwaniami:
ale jeśli dodasz macierz
x <- c(x, matrix(5,2,2)
, twoja lista będzie miała jeszcze 4 elementy wartości5
! Lepiej wykonaj:Działa dla każdego innego obiektu, a otrzymasz zgodnie z oczekiwaniami:
Wreszcie twoja funkcja staje się:
i działa na każdym typie obiektu. Możesz być mądrzejszy i robić:
źródło
Jest również
list.append
zrlist
( link do dokumentacji )To bardzo proste i wydajne.
źródło
c()
lublist
. Oba są znacznie szybsze.rlist::list.append()
, jest to zasadniczo opakowaniebase::c()
.W celu weryfikacji uruchomiłem kod testu dostarczony przez @Cron. Jest jedna główna różnica (oprócz szybszego działania na nowszym procesorze i7):
by_index
teraz działa prawie tak dobrze, jaklist_
:W celach informacyjnych znajduje się kod testu porównawczego skopiowany dosłownie z odpowiedzi @ Cron (na wypadek, gdyby później zmienił treść):
źródło
źródło
To bardzo interesujące pytanie i mam nadzieję, że moja poniższa myśl może przyczynić się do rozwiązania tego problemu. Ta metoda daje płaską listę bez indeksowania, ale ma listę i listę, aby uniknąć zagnieżdżania struktur. Nie jestem pewien szybkości, ponieważ nie wiem, jak ją przeprowadzić.
źródło
mylist<-list(1,2,3) mylist<-c(mylist,list(5))
Możemy więc łatwo dołączyć element / obiekt za pomocą powyższego kodu
źródło