Wir hängen ein Objekt zu einer Liste in R in amortisiert konstanter Zeit, O(1)?
Wenn ich einige R-Liste mylist
können Sie hängen ein Element obj
zu es so auf:
mylist[[length(mylist)+1]] <- obj
Aber sicherlich gibt es einige mehr kompakt. Wenn ich war neu an der R, habe ich versucht zu schreiben lappend()
etwa so:
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
aber natürlich nicht, die Arbeit wegen der R-call-by-name-Semantik (lst
ist effektiv kopiert beim Aufruf, so dass änderungen an lst
sind nicht sichtbar außerhalb des Anwendungsbereichs der lappend()
. Ich weiß, Sie tun können, Umwelt-hacking in eine R-Funktion zu erreichen außerhalb des Bereichs der Ihre Funktion und mutieren die aufrufende Umgebung, aber das scheint wie ein großer hammer auf das schreiben einer einfachen append-Funktion.
Kann jeder jeden schlagen, ein schöner Weg, dies zu tun? Bonus Punkte, wenn es funktioniert für beide Vektoren und Listen.
- R hat die unveränderliche Daten, Merkmale, die Häufig in funktionalen Sprachen, hasse es dies zu sagen, aber ich denke, Sie müssen nur mit ihm zu beschäftigen. Es hat seine vor-und seine Nachteile
- Wenn Sie sagen, "call-by-name" Sie wirklich meinen "call-by-value", richtig?
- Nein, es ist definitiv kein call-by-value, ansonsten wäre das kein problem. R eigentlich mit call-by-need (en.wikipedia.org/wiki/Evaluation_strategy#Call_by_need).
- Eine gute Idee ist, die pre-teilen Sie Ihre vector/list: N = 100 mylist = vector ("list", N) for (i in 1:N) { #mylist[[i]] = ... } Vermeiden Sie 'wächst' objects in R.
- Ich habe versehentlich die Antwort hier, stackoverflow.com/questions/17046336/... So schwer zu implementieren, so dass einfache Algorithmus!
- ok, aber das ist nicht allgemein anwendbar, es funktioniert nur in Szenarien, in denen können Sie wissen, die genaue endgültige Länge im Voraus (oder vielleicht Quietschen durch in können Sie eine Obere Schranke ist).
- die
lappend
Ansatz hat den Vorteil, das Auffüllen einer leeren Liste (in der Regel innerhalb einer Schleife).l1 = list(l1, l2)
, beginnend mit einer leeren Liste, der stellt ein leeres element als erstes element. - Kurze Antwort: R hat keine built-in-Daten-Struktur, die unterstützt das Ziel, bereits im Titel. Siehe JanKanis Antwort für eine benutzerdefinierte Implementierung.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn es eine Liste von Strings, benutzen Sie einfach die
c()
Funktion :Funktioniert bei Vektoren auch, so bekomme ich die Bonuspunkte?
Bearbeiten (2015-Feb-01): Dieser Beitrag ist demnächst auf seinen fünften Geburtstag. Einige freundlichen Leser immer wiederholen etwaige Mängel mit, so mit allen Mitteln, siehe auch einige der Kommentare unten. Ein Vorschlag für
list
Typen:Im Allgemeinen, R-Typen können machen es schwer zu haben und nur eine Redewendung für alle Arten und Verwendung.
LL
weiterhin zwei Elemente nachC(LL, c="harry")
genannt wird.LL <- c(LL, c="harry")
.c()
undappend()
(und dielappend()
habe ich gestellt), alle weisen von O(n^2) Verhalten. Ich hatte gehofft, für O(n lg n), das ist, was Sie erhalten würden, mit einer dynamisch wachsenden Sequenz-Daten-Struktur in den meisten Sprachen, aber es scheint nicht, wie R hat, dass zumindest erbaut.c(list(a=3, b=c(4, 5)), c=c(6, 7))
ist atemberaubend.identical(c(list(a=3, b=c(4, 5), c=c(6, 7))), list(a=3, b=c(4, 5), c=c(6, 7)))
und sehen, dass Sie das gleiche-diec(...)
rund um dielist()
ist ein null-op. Also, was genau ist schlimm hier?c()
hat 2 Argumente: die Liste, die ich versuche anzuhängen, nämlichlist(a=3, b=c(4, 5))
, und den Artikel, den ich versuche, zum Anhängen, nämlichc=c(6, 7)
. Wenn Sie mein Ansatz, Sie werden sehen, dass 2 Elemente der Liste angehängt (6
und7
mit Namenc1
undc2
) anstelle einer 2-element-Vektor namensc
da ist eindeutig bestimmt!NULL
, obwohl, dass ist eher unwahrscheinlich.v <- c(); for (i in 1:n) v <- c(v, i)
Wie ich weiterhin mit Doppel-n, die Zeit, die Vierzeiler. Dies ist also nicht das, was der OP verlangt (amortisiert konstanter Zeit Anhängen).mylist <- list(mylist, list(obj))
? Wenn ja, wäre es nett, die Antwort zu ändernc()
sich Punkte heraus, die Probleme bei derobj
ist ein Vektor. Meine vorgeschlagene Korrektur/Klarstellung entlang dieser Linien wurde abgelehnt, da "macht keinen Sinn, weil ein Bearbeiten."newlist <- list(oldlist, list(someobj))
scheint, dass die neue Teilliste rekursiv zu verbinden, die oldlist. undc(LL, c="harry")
funktioniert nur für strings, eine Liste von Nummern.lst[[length(lst)+1]] <- obj
richtig funktioniert für mich.Ent = list(con = list(1:3)); con = 2:4; Ent$con = c(Ent$con, list(con)); con = 3:5; Ent$con = c(Ent$con, list(con))
dies funktioniert für mich. Liste, Teilliste---- - VektorDer OP (im April 2012 aktualisierte revision nicht in Frage) ist daran interessiert zu wissen, wenn es einen Weg gibt, um das hinzufügen zu einer Liste in amortisiert konstanter Zeit, wie getan werden kann, zum Beispiel mit einem C++ -
vector<>
container. Die beste Antwort(en?) hier bisher nur die relativen Ausführungszeiten für verschiedene Lösungen gegeben eine Feste Größe-problem, aber nicht die Adresse eine der verschiedenen Lösungen' Algorithmische Effizienz direkt. Kommentare unten viele der Antworten, besprechen der algorithmischen Effizienz der einige der Lösungen, aber in jedem Fall der Zeitpunkt (ab April 2015) Sie kommen zu der falschen Schlussfolgerung.Algorithmische Effizienz fängt das Wachstum Eigenschaften, entweder in der Zeit (Ausführungszeit) oder Raum (Speicherverbrauch) als ein problem der Größe wächst. Läuft ein performance-test für verschiedene Lösungen gegeben eine Feste Größe-problem wird nicht auf die verschiedenen Lösungen Wachstumsrate. Der OP ist daran interessiert zu wissen, ob es eine Möglichkeit zum Anhängen von Objekten an ein R-Liste in der "amortized constant time". Was bedeutet das? Um zu erklären, lassen Sie mich zuerst beschreiben, "Konstante Zeit":
Konstante oder O(1) Wachstum:
Wenn die Zeit, die erforderlich ist zur Erfüllung einer bestimmten Aufgabe bleibt die gleiche als die Größe des Problems verdoppelt, dann sagt man, der Algorithmus Exponate konstanter Zeit Wachstum, oder erklärt, im "Big-O" - notation, Exponate O(1) Zeit Wachstum. Wenn der OP sagt "abgeschrieben" Konstante Zeit, er bedeutet einfach nur "in the long run"... also, wenn die Durchführung einer einzigen operation gelegentlich dauert viel länger als normal (z.B. wenn eine Feste Puffer erschöpft ist und gelegentlich erfordert eine Anpassung durch einen größeren Puffer), solange die langfristige Durchschnittliche Leistung ist konstanter Zeit, wir werden es immer noch O(1).
Für den Vergleich, ich werde auch beschreiben, "lineare Zeit" und "in quadratischer Zeit":
Lineare oder O(n) Wachstum:
Wenn die Zeit, die erforderlich ist zur Erfüllung einer bestimmten Aufgabe verdoppelt als die Größe des Problems verdoppelt, dann sagt man, der Algorithmus Exponate die lineare Zeit, oder O(n) Wachstum.
Quadratische oder O(n2) Wachstum:
Wenn die Zeit, die erforderlich ist zur Erfüllung einer bestimmten Aufgabe erhöht sich mit dem Quadrat der problem-Größe, wir sagen, der Algorithmus Exponate quadratische Zeit, oder O(n2) Wachstum.
Gibt es viele andere Effizienz-Klassen von algorithmen; ich aufschieben, um die Wikipedia-Artikel für die weitere Diskussion.
Ich danke @CronAcronis für seine Antwort, als ich bin neu in R und es war schön, eine voll konstruierte code-block, der für eine performance-Analyse der verschiedenen Lösungen präsentiert auf dieser Seite. Ich bin Kreditaufnahme seinen code für meine Analyse, die ich duplizieren (eingewickelt in einer Funktion) unten:
Die Ergebnisse gepostet von @CronAcronis scheinen definitiv zu vermuten, dass die
a <- list(a, list(i))
Methode ist am schnellsten, zumindest für ein problem der Größe von 10000, aber die Ergebnisse für ein einzelnes problem Größe tun, nicht auf das Wachstum der Lösung. Für die, die wir ausführen müssen, um ein minimum von zwei profiling-tests, mit unterschiedlichen Größen problem:Erstens, ein Wort über die min/lq/mean/median/uq/max-Werte: Da wir uns an die exakt gleiche Aufgabe für jeden der 5 läuft, in einer idealen Welt könnten wir erwarten, dass es dauern würde, genau die gleiche Menge an Zeit für jeden Lauf. Aber der erste Lauf ist normalerweise vorgespannt in Richtung auf längere Zeit aufgrund der Tatsache, dass der code, den wir hier testen, ist noch nicht geladen, in den cache der CPU. Nach dem ersten Lauf, würden wir erwarten, dass die Zeiten relativ konstant, aber gelegentlich unser code kann entfernt werden aus dem cache aufgrund der timer-tick-interrupts oder hardware-interrupts, die keinen Bezug zum code, den wir testen. Testen Sie die code-snippets, 5-mal, wir werden so dass der code in den cache geladen beim ersten mal und dann jedes snippet 4 Chancen bis zum Abschluss ausgeführt, ohne Einmischung von außen-Veranstaltungen. Aus diesem Grund, und weil wir auch wirklich die exakt gleiche code unter den exakt gleichen eingangsbedingungen jedes mal, betrachten wir nur die 'min' mal ausreichend zu sein für die besten Vergleich zwischen den verschiedenen code-Optionen.
Beachten Sie, dass ich entschied mich für den ersten Lauf mit einem problem der Größe von 2000 und 20000, also mein problem vergrößert um einen Faktor von 10 aus dem ersten Lauf auf den zweiten.
Leistung der
list
Lösung: O(1) (Konstante Zeit)Lassen Sie uns zuerst einen Blick auf die Entwicklung der
list
Lösung, da wir sofort erkennen kann, dass es die Schnellste Lösung in beiden profiling läuft: Im ersten Lauf, es dauerte 854 microSekunden (0.854 milliSekunden), um 2000 "append" - Aufgaben. Im zweiten Durchgang dauerte es 8.746 Millisekunden durchführen 20000 "append" - Aufgaben. Ein naiver Beobachter würde sagen, "Ah, dielist
- Lösung Exponate O(n) Wachstum, da das problem der Größe wuchs um den Faktor zehn, so hat die Zeit, die erforderlich ist, um den test auszuführen." Das problem mit dieser Analyse ist, dass das, was der OP will, ist die Wachstumsrate der ein einzelnes Objekt einfügen, nicht die Wachstumsrate des Gesamt-Problems. Wissen, dass, es ist klar, dass dann dielist
Lösung bietet genau das, was der OP will: eine Methode zum anfügen von Objekten zu einer Liste in O(1) Zeit.Leistung von anderen Lösungen
Keine der anderen Lösungen auch nur annähernd die Geschwindigkeit des
list
Lösung, aber es ist lehrreich, zu prüfen, Sie trotzdem:Meisten anderen Lösungen zu sein scheinen, O(n) in der Leistung. Zum Beispiel, die
by_index
Lösung, eine sehr beliebte Lösung, basierend auf der Häufigkeit, mit der ich es in anderen posts, nahm 11.6 Millisekunden Anhängen 2000 Objekte und 953 Millisekunden angefügt zehn mal, dass viele Objekte. Das Allgemeine problem ist an der Zeit wuchs um den Faktor 100, so daß ein naiver Beobachter könnte sagen "Ah, dieby_index
- Lösung Exponate O(n2) Wachstum, da das problem der Größe wuchs um den Faktor zehn, die Zeit, die erforderlich ist, um den test auszuführen wuchs um den Faktor 100." Nach wie vor, diese Analyse ist fehlerhaft, da die OP ist daran interessiert, das Wachstum eines einzelnen Objekts einfügen. Wenn wir unterteilen die gesamte Zeit Wachstum mit dem problem, die Größe, das Wachstum, finden wir, dass der Zeit-Wachstum von Anhängen Objekte stieg um einen Faktor von nur 10, nicht einen Faktor von 100, das entspricht dem Wachstum der problem-Größe, so dass dieby_index
Lösung ist O(n). Es gibt keine Lösungen aufgeführt, die zeigen O(n2) Wachstum für das anfügen eines einzelnen Objekts.In der Lisp-wir haben es auf diese Weise:
war, obwohl es "wider", nicht nur "c". Wenn Sie brauchen, um zu starten mit einem empy-Liste, verwenden Sie l <- NULL.
l <- c(l, 2)
funktioniert ohne die Notwendigkeit fürrev(l)
c()
Funktion kopiert seine Argumente in einen neuen Vektor/Liste zurückgegeben.Du willst so etwas wie das vielleicht?
Es ist nicht sehr höflich Funktion (Zuordnung zu
parent.frame()
ist sowas von unhöflich), aber IIUYC es ist, was Sie für Fragen.Wenn Sie in der Liste variable als Zeichenfolge in Anführungszeichen, können Sie es erreichen von innerhalb der Funktion wie:
also:
oder für extra credit:
Habe ich mal einen kleinen Vergleich von Methoden, die hier erwähnt.
Ergebnisse:
list = list
wurden nicht nur die Sieger, sondern von 1 bis 2 Aufträge oder Größe!Nicht sicher, warum Sie glaube nicht, dass Ihre erste Methode nicht funktionieren. Sie haben einen Fehler in der Funktion lappend: Länge(Liste), sollte die Länge(lst). Dies funktioniert gut und gibt eine Liste mit den angehängten obj.
lappend()
dass habe ich und es scheint zu führen ungefähr so gut wie c() und append(), alle weisen von O(n^2) Verhalten.versuchen, diese Funktion lappend
und andere Anregungen von dieser Seite Fügen Sie namens-Vektor in eine Liste
Bye.
Ich denke, was Sie tun möchten, ist eigentlich übergabe per Referenz (Zeiger) an die Funktion-- erstellen einer neuen Umgebung (die per Referenz übergeben Funktionen) mit der Liste Hinzugefügt:
Nun sind Sie nur einer änderung der bestehenden Liste (nicht einen neuen zu erstellen)
Dies ist eine einfache Möglichkeit zum hinzufügen von Elementen zu einem R-Liste:
Oder programmatisch:
append()
Funktion, aber es ist wirklich eine verketten-Funktion, und es funktioniert nur bei Vektoren.append()
arbeitet auf Vektoren und Listen, und es ist eine wahre append (das ist im Grunde das gleiche wie verketten, so sehe ich nicht, was dein problem ist)in der Tat ist es subtelty mit der
c()
Funktion. Wenn Sie das tun:erhalten Sie wie erwartet:
aber wenn Sie fügen Sie eine matrix mit
x <- c(x, matrix(5,2,2)
Ihrer Liste haben, die anderen 4 Elemente der Wert5
!Sie würde besser tun:
Es funktioniert für jedes andere Objekt und Sie erhalten als erwartet:
Endlich, Ihre Funktion wird:
und es funktioniert für jede Art von Objekt. Sie können klüger sein und tun:
Gibt es auch
list.append
von derrlist
(link zur Dokumentation)Es ist sehr einfach und effizient.
c()
oderlist
-Methode. Beide sind viel schneller.rlist::list.append()
, es ist im wesentlichen ein wrapper umbase::c()
.Zur Validierung mir lief der benchmark-code von @Cron. Es gibt einen wichtigen Unterschied (neben der Ausführung schneller auf die neuere i7 Prozessor): die
by_index
führt jetzt fast so gut wie dielist_
:Referenz hier ist der benchmark-code kopiert wortwörtlich von @Cron-Antwort (nur für den Fall, dass er später ändert den Inhalt):
Dies ist eine sehr interessante Frage, und ich hoffe, dass meine Gedanken unten könnte dazu beitragen, eine Art von Lösung für Sie. Diese Methode, die geben eine flache Liste ohne Indexierung, aber es funktioniert Liste und unlist zu vermeiden, die Verschachtelung von Strukturen. Ich bin mir nicht sicher über die Geschwindigkeit, da ich nicht weiß, wie es zum benchmark.
mylist<-list(1,2,3)
mylist<-c(mylist,list(5))
So können wir leicht append-element/Objekt mit dem obigen code