Was sind die Unterschiede zwischen den Zuweisungsoperatoren "=" und "<-" in R?

712

Was sind die Unterschiede zwischen den Zuweisungsoperatoren =und <-in R?

Ich weiß, dass die Operatoren etwas anders sind, wie dieses Beispiel zeigt

x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"

Aber ist das der einzige Unterschied?

csgillespie
quelle
45
Wie hier erwähnt, stammen die Ursprünge des <-Symbols von alten APL-Tastaturen, auf denen sich tatsächlich eine einzige <-Taste befand.
Joran

Antworten:

97

Was sind die Unterschiede zwischen den Zuweisungsoperatoren =und <-in R?

Als Ihr Beispiel zeigt, =und <-etwas anderen Operator Vorrang hat (was die Reihenfolge der Auswertung bestimmt , wann sie in dem gleichen Ausdruck gemischt werden). Tatsächlich gibt ?Syntaxin R die folgende Operator-Prioritätstabelle vom höchsten zum niedrigsten an:

…
‘-> ->>’           rightwards assignment
‘<- <<-’           assignment (right to left)=’                assignment (right to left)

Aber ist das der einzige Unterschied?

Da Sie nach den Zuweisungsoperatoren gefragt haben : Ja, das ist der einzige Unterschied. Es wird Ihnen jedoch vergeben, wenn Sie etwas anderes glauben. Sogar die R-Dokumentation von ?assignOpsBehauptungen, dass es mehr Unterschiede gibt:

Der Operator <-kann überall verwendet werden, während der Operator =nur auf der obersten Ebene (z. B. in dem an der Eingabeaufforderung eingegebenen vollständigen Ausdruck) oder als einer der Unterausdrücke in einer geschweiften Liste von Ausdrücken zulässig ist.

Lassen Sie uns nicht zu genau darauf eingehen: Die R-Dokumentation ist (subtil) falsch [ 1 ] . Dies ist leicht zu zeigen: Wir müssen nur ein Gegenbeispiel für den =Operator finden, das sich nicht (a) auf der obersten Ebene befindet, noch (b) einen Unterausdruck in einer geschweiften Liste von Ausdrücken (dh {…; …}). - Ohne weiteres:

x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1

Natürlich haben wir eine Aufgabe =außerhalb der Kontexte (a) und (b) durchgeführt. Warum ist die Dokumentation eines Kernmerkmals der R-Sprache seit Jahrzehnten falsch?

=Dies liegt daran, dass das Symbol in Rs Syntax zwei unterschiedliche Bedeutungen hat, die routinemäßig zusammengeführt werden:

  1. Die erste Bedeutung ist als Zuweisungsoperator . Das ist alles, worüber wir bisher gesprochen haben.
  2. Die zweite Bedeutung ist kein Operator, sondern ein Syntax-Token , das das Übergeben eines benannten Arguments in einem Funktionsaufruf signalisiert . Im Gegensatz zum =Operator, der zur Laufzeit keine Aktion ausführt, ändert er lediglich die Art und Weise, wie ein Ausdruck analysiert wird.

Wir werden sehen.

In jedem Code der allgemeinen Form…

‹function_name›(‹argname› = ‹value›,)
‹function_name›(‹args›, ‹argname› = ‹value›,)

… Das =ist das Token, das die Übergabe benannter Argumente definiert: Es ist nicht der Zuweisungsoperator. Darüber hinaus =ist in einigen syntaktischen Kontexten völlig verboten :

if (‹var› = ‹value›) …
while (‹var› = ‹value›) …
for (‹var› = ‹value› in ‹value2›) …
for (‹var1› in ‹var2› = ‹value›) …

Jedes dieser Ereignisse löst den Fehler "unerwartet '=' in ‹bla›" aus.

=Bezieht sich in jedem anderen Kontext auf den Aufruf des Zuweisungsoperators. Insbesondere das bloße Setzen von Klammern um den Unterausdruck macht eine der oben genannten (a) und (b) eine Zuweisung gültig . Zum Beispiel führt das Folgende eine Zuweisung durch:

median((x = 1 : 10))

Aber auch:

if (! (nf = length(from))) return()

Jetzt könnten Sie einwenden, dass ein solcher Code grausam ist (und Sie könnten Recht haben). Aber ich nahm diesen Code aus der base::file.copyFunktion (ersetzt <-mit= ) - es ist ein durchdringendes Muster in weiten Teilen der Kern R - Codebasis.

Die ursprüngliche Erklärung von John Chambers , auf der die R-Dokumentation wahrscheinlich basiert, erklärt dies tatsächlich richtig:

[ =Zuweisung ist] nur an zwei Stellen in der Grammatik zulässig: auf der obersten Ebene (als vollständiges Programm oder vom Benutzer eingegebener Ausdruck); und wenn von der umgebenden logischen Struktur isoliert, durch Klammern oder ein zusätzliches Paar Klammern.


Ein Geständnis: Ich habe früher gelogen. Es gibt einen zusätzlichen Unterschied zwischen den Operatoren =und <-: Sie rufen unterschiedliche Funktionen auf. Standardmäßig machen diese Funktionen dasselbe, aber Sie können beide separat überschreiben, um das Verhalten zu ändern. Im Gegensatz dazu rufen <-und ->(Zuordnung von links nach rechts), obwohl syntaktisch unterschiedlich, immer dieselbe Funktion auf. Das Überschreiben eines überschreibt auch das andere. Das zu wissen ist selten praktisch , kann aber für ein paar lustige Spielereien verwendet werden .

Konrad Rudolph
quelle
1
In Bezug auf den Vorrang und die Fehler in Rs Dokument liegt der Vorrang von ?tatsächlich zwischen =und <-, was wichtige Konsequenzen beim Überschreiben hat ? , und praktisch keine anderen.
Moody_Mudskipper
@Moody_Mudskipper das ist bizarr! Sie scheinen Recht zu haben, aber laut Quellcode ( main/gram.y) ist die Priorität von ?korrekt dokumentiert und niedriger als beide =und <-.
Konrad Rudolph
Ich spreche kein C, aber ich nehme an, dass =Sie eine Sonderbehandlung erhalten, bevor der Analysebaum erstellt wird. Möglicherweise im Zusammenhang mit Funktionsargumenten ist es sinnvoll, nach dem Rest des Ausdrucks zu foo(x = a ? b)suchen, =bevor wir ihn analysieren.
Moody_Mudskipper
@Moody_Mudskipper Ich habe r-devel gefragt
Konrad Rudolph
2
@Moody_Mudskipper FWIW dies ist endlich in 4.0.0 behoben.
Konrad Rudolph
661

Der Unterschied zwischen Zuweisungsoperatoren wird deutlicher, wenn Sie sie zum Festlegen eines Argumentwerts in einem Funktionsaufruf verwenden. Zum Beispiel:

median(x = 1:10)
x   
## Error: object 'x' not found

In diesem Fall xwird im Rahmen der Funktion deklariert, sodass sie im Benutzerarbeitsbereich nicht vorhanden ist.

median(x <- 1:10)
x    
## [1]  1  2  3  4  5  6  7  8  9 10

In diesem Fall xwird im Benutzerarbeitsbereich deklariert, sodass Sie ihn nach Abschluss des Funktionsaufrufs verwenden können.


In der R-Community wird die <-Kompatibilität mit (sehr) alten Versionen von S-Plus generell für die Zuweisung (außer für Funktionssignaturen) bevorzugt . Beachten Sie, dass die Leerzeichen helfen, Situationen wie zu klären

x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3

Die meisten R-IDEs verfügen über Tastaturkürzel, um die Eingabe zu <-vereinfachen. Ctrl+ =in Architect, Alt+ -in RStudio ( Option+ -unter macOS), Shift+ -(Unterstrich) in emacs + ESS.


Wenn Sie mit dem Schreiben vorziehen =zu <-wollen aber für öffentlich veröffentlichten Code (auf CRAN, zum Beispiel) , das häufigen Zuordnung Symbol verwenden, dann können Sie eine der Verwendung tidy_*Funktionen im formatRPaket automatisch zu ersetzen =mit <-.

library(formatR)
tidy_source(text = "x=1:5", arrow = TRUE)
## x <- 1:5

Die Antwort auf die Frage "Warum x <- y = 5wirft ein Fehler aber nicht x <- y <- 5?" ist "Es liegt an der Magie, die im Parser enthalten ist". Die Syntax von R enthält viele mehrdeutige Fälle , die auf die eine oder andere Weise gelöst werden müssen. Der Parser entscheidet sich dafür, die Bits des Ausdrucks in unterschiedlicher Reihenfolge aufzulösen, je nachdem, ob =oder<- nicht.

Um zu verstehen, was passiert, müssen Sie wissen, dass die Zuweisung den zugewiesenen Wert stillschweigend zurückgibt. Sie können dies deutlicher erkennen, indem Sie beispielsweise explizit drucken print(x <- 2 + 3).

Zweitens ist es klarer, wenn wir die Präfixnotation für die Zuweisung verwenden. Damit

x <- 5
`<-`(x, 5)  #same thing

y = 5
`=`(y, 5)   #also the same thing

Der Parser interpretiert x <- y <- 5als

`<-`(x, `<-`(y, 5))

Man könnte erwarten , dass x <- y = 5wäre dann

`<-`(x, `=`(y, 5))

aber eigentlich wird es interpretiert als

`=`(`<-`(x, y), 5)

Dies liegt daran, dass die =Priorität niedriger <-ist als auf der ?SyntaxHilfeseite angegeben.

Richie Cotton
quelle
4
Dies wird auch in Kapitel 8.2.26 von The R Inferno von Patrick Burns erwähnt (Nicht ich, aber trotzdem eine Empfehlung)
Uwe
3
Hat median((x = 1:10))jedoch den gleichen Effekt wie median(x <- 1:10).
Francesco Napolitano
2
Ich betrachte sie nicht wirklich als Abkürzungen, auf jeden Fall
drückst
5
Ich habe gerade festgestellt, dass Ihre Erklärung, wie x <- x = 5interpretiert wird, etwas falsch ist: In Wirklichkeit interpretiert R sie als ​`<-<-`(x, y = 5, value = 5)(was selbst mehr oder weniger gleichwertig ist tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5)). Huch!
Konrad Rudolph
4
… Und ich habe gerade festgestellt, dass der allererste Teil dieser Antwort falsch und leider ziemlich irreführend ist, weil er ein weit verbreitetes Missverständnis aufrechterhält: Die Art und Weise, wie Sie =in einem Funktionsaufruf verwenden, führt keine Zuweisung durch und ist kein Zuweisungsoperator. Es ist ein völlig unterschiedlicher analysierter R-Ausdruck, der zufällig dasselbe Zeichen verwendet. Außerdem wird der von Ihnen angezeigte Code xim Funktionsumfang nicht "deklariert" . Die Funktionsdeklaration führt diese Deklaration aus. Der Funktionsaufruf funktioniert nicht (mit benannten ...Argumenten wird es etwas komplizierter ).
Konrad Rudolph
103

Der R-Styleguide von Google vereinfacht das Problem, indem das "=" für die Zuweisung gesperrt wird. Keine schlechte Wahl.

https://google.github.io/styleguide/Rguide.xml

Das R-Handbuch geht detailliert auf alle 5 Zuweisungsoperatoren ein.

http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html

Nosredna
quelle
133
Der Nachteil einer zufälligen Zuordnung, x<-ywann x < -ygemeint war, ärgert mich so sehr, dass ich es persönlich vorziehe =. Es scheint mir nicht gut zu sein, wenn Ihr Code davon abhängt, dass Leerzeichen vorhanden sind. Es ist in Ordnung, Abstände als Stilempfehlung vorzuschlagen, aber damit Ihr Code anders ausgeführt wird, unabhängig davon, ob ein Leerzeichen vorhanden ist oder nicht. Was passiert, wenn Sie Ihren Code neu formatieren oder Suchen und Ersetzen verwenden, kann das Leerzeichen manchmal verschwinden und der Code geht schief. Das ist kein Problem mit =. IIUC, Verbot bedeutet =, " <- " zu verlangen ; dh 3 Zeichen einschließlich eines Leerzeichens, nicht nur " <-".
Matt Dowle
12
Beachten Sie, dass jedes Nicht-0 TRUEvon R berücksichtigt wird. Wenn Sie also testen möchten, ob xes kleiner als ist -y, schreiben Sie möglicherweise, if (x<-y)was nicht warnt oder fehlerhaft ist und anscheinend einwandfrei funktioniert. Es wird nur sein , FALSEwenn y=0, wenn.
Matt Dowle
4
Wenn Sie verbieten =und verwenden, <- ist es schwer zu argumentieren, dass ein zusätzlicher Schritt von grep "[^<]<-[^ ]" *.Rnicht erforderlich ist. =braucht so einen nicht grep.
Matt Dowle
34
Warum verletzen Sie Ihre Augen und Finger mit, <-wenn Sie verwenden können =? In 99,99% der Fälle =ist in Ordnung. Manchmal braucht man <<-aber, was eine andere Geschichte ist.
Fernando
10
Der Fokus auf <- ist vielleicht einer der lahmen Gründe für das Fehlen von + = und - =.
Chris
37

x = y = 5ist äquivalent zu x = (y = 5), weil die Zuweisungsoperatoren von rechts nach links "gruppieren", was funktioniert. Bedeutung: Weisen Sie 5 zu yund lassen Sie die Nummer 5; und dann diese 5 zuweisen x.

Dies ist nicht dasselbe wie (x = y) = 5, was nicht funktioniert! Bedeutung: Weisen Sie den Wert von ybis zu xund lassen Sie den Wert vony ; und dann 5 zuweisen, umm ..., was genau?

Wenn Sie die verschiedenen Arten von Zuweisungsoperatoren mischen, werden die <-Bindungen enger als =. So x = y <- 5wird interpretiert x = (y <- 5), was sinnvoll ist.

Wird leider x <- y = 5als interpretiert (x <- y) = 5, was der Fall ist, der nicht funktioniert!

Siehe ?Syntaxund ?assignOpsfür die Vorrang- (Bindungs-) und Gruppierungsregeln.

Steve Pitchers
quelle
Ja, wie Konrad Rudolphs Antwort sagte, <- <<-steht oben = in <-der Rangliste , was bedeutet , dass zuerst ausgeführt wird. Also x <- y = 5sollte ausgeführt werden als (x <- y) = 5.
Nick Dong
1
@ Nick Dong Ja in der Tat. Hilfreicherweise ist die Operator-Prioritätstabelle in ? Syntax {base} eindeutig dokumentiert .
Steve Pitchers
33

Laut John Chambers ist der Bediener =nur auf "der obersten Ebene" erlaubt, was bedeutet, dass er in Kontrollstrukturen wie nicht erlaubt ist if, was den folgenden Programmierfehler illegal macht.

> if(x = 0) 1 else x
Error: syntax error

Wie er schreibt: "Wenn Sie das neue Zuweisungsformular [=] in Steuerausdrücken nicht zulassen, werden Programmierfehler (wie im obigen Beispiel) vermieden, die beim gleichen Operator wahrscheinlicher sind als bei anderen S-Zuweisungen."

Sie können dies tun, wenn es "durch Klammern oder ein zusätzliches Klammerpaar von der umgebenden logischen Struktur isoliert" ist if ((x = 0)) 1 else x.

Siehe http://developer.r-project.org/equalAssign.html

Aaron verließ Stack Overflow
quelle
11
Es ist ein häufiger Fehler, der x==0fast immer gemeint ist.
Aaron verließ Stack Overflow
14
Ah ja, ich habe übersehen, dass Sie "Programmierfehler" gesagt haben. Es ist eigentlich eine gute Nachricht, dass dies einen Fehler verursacht. Und ein guter Grund, lieber x=0als Aufgabe zu sein x<-0!
Steve Pitchers
7
Ja, es ist schön, dass dies einen Fehler verursacht, obwohl ich eine andere Lektion darüber ziehe, was ich bevorzugen soll. Ich beschließe, =so wenig wie möglich zu verwenden, weil =und ==so ähnlich aussehen.
Aaron verließ Stack Overflow
2
Die Art und Weise, wie dieses Beispiel dargestellt wird, ist für mich so seltsam. if(x = 0) 1 else xlöst einen Fehler aus und hilft mir, einen Fehler zu finden und zu korrigieren. if(x <- 1) 1 else xwirft keinen Fehler und ist sehr verwirrend.
Gregor Thomas
3
Ich meine, ein wirklich hilfreicher Fehlerprüfer würde dort einen Fehler auslösen und sagen "Sie haben nutzlosen Code, der immer den elseWert zurückgibt, wollten Sie ihn so schreiben?", Aber das könnte ein Wunschtraum sein ...
TylerH
26

Die Operatoren <-und =ordnen der Umgebung zu, in der sie ausgewertet werden. Der Operator <-kann überall verwendet werden, während der Operator =nur auf der obersten Ebene (z. B. in dem an der Eingabeaufforderung eingegebenen vollständigen Ausdruck) oder als einer der Unterausdrücke in einer geschweiften Liste von Ausdrücken zulässig ist .

Haim Evgi
quelle
8
Ich denke, "oberste Ebene" bedeutet eher auf Anweisungsebene als auf Ausdrucksebene. So x <- 42auf seine eigene ist eine Aussage; in if (x <- 42) {}wäre es ein Ausdruck, und ist nicht gültig. Dies hat natürlich nichts damit zu tun, ob Sie sich in der globalen Umgebung befinden oder nicht.
Steve Pitchers
1
Dies: "Der Bediener = ist nur auf der obersten Ebene zulässig" ist ein weit verbreitetes Missverständnis und völlig falsch.
Konrad Rudolph
Dies ist nicht wahr - zum Beispiel funktioniert dies, obwohl die Zuweisung kein vollständiger Ausdruck ist:1 + (x = 2)
Pavel Minaev
1
Um die Kommentare von KonradRudolph und PavelMinaev zu verdeutlichen, denke ich, dass es zu stark ist, um zu sagen, dass es völlig falsch ist, aber es gibt eine Ausnahme, wenn es "durch Klammern oder ein zusätzliches Klammerpaar von der umgebenden logischen Struktur isoliert" ist.
Aaron verließ Stack Overflow
Oder function() x = 1, repeat x = 1, if (TRUE) x = 1....
Moody_Mudskipper
6

Dies kann auch zum Verständnis des Unterschieds zwischen diesen beiden Operatoren beitragen:

df <- data.frame(
      a = rnorm(10),
      b <- rnorm(10)
)

Für das erste Element hat R Werte und Eigennamen zugewiesen, während der Name des zweiten Elements etwas seltsam aussieht.

str(df)
# 'data.frame': 10 obs. of  2 variables:
#  $ a             : num  0.6393 1.125 -1.2514 0.0729 -1.3292 ...
#  $ b....rnorm.10.: num  0.2485 0.0391 -1.6532 -0.3366 1.1951 ...

R Version 3.3.2 (31.10.2016); macOS Sierra 10.12.1

Denis Rasulev
quelle
6
Können Sie genauer erklären, warum dies passiert / was hier los ist? (Hinweis: data.frameversucht, den Namen der angegebenen Variablen als Namen des Elements im
Datenrahmen zu verwenden
Ich dachte nur, könnte das möglicherweise ein Fehler sein? Und wenn ja, wie und wo melde ich das?
Denis Rasulev
7
Es ist kein Fehler. Ich habe versucht, auf die Antwort in meinem obigen Kommentar hinzuweisen. Beim Festlegen des Namens des Elements verwendet R das Äquivalent von make.names("b <- rnorm(10)").
Ben Bolker