Quali sono le differenze tra gli operatori di assegnazione “=” e “<-” in R?


712

Quali sono le differenze tra gli operatori di assegnazione =e <-in R?

So che gli operatori sono leggermente diversi, come mostra questo esempio

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

Ma questa è l'unica differenza?


45
Come notato qui le origini del <-simbolo provengono da vecchie tastiere APL che in realtà avevano un solo <-tasto su di esse.
joran,

Risposte:


97

Quali sono le differenze tra gli operatori di assegnazione =e <-in R?

Come mostra il tuo esempio, =e <-hanno una precedenza leggermente diversa dall'operatore (che determina l'ordine di valutazione quando sono mescolati nella stessa espressione). In effetti, ?Syntaxin R fornisce la seguente tabella di precedenza degli operatori, dal più alto al più basso:

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

Ma questa è l'unica differenza?

Dato che stavi chiedendo degli operatori di assegnazione : sì, questa è l'unica differenza. Tuttavia, verrai perdonato per aver creduto diversamente. Anche la documentazione R ?assignOpsafferma che esistono più differenze:

L'operatore <-può essere utilizzato ovunque, mentre l'operatore =è consentito solo al livello superiore (ad esempio, nell'espressione completa digitata al prompt dei comandi) o come una delle sottoespressioni in un elenco di espressioni rinforzate.

Non mettiamoci un punto troppo fine: la documentazione R è (sottilmente) sbagliata [ 1 ] . Questo è facile da mostrare: dobbiamo solo trovare un contro-esempio =dell'operatore che non sia (a) al livello più alto, né (b) una sottoespressione in un elenco rinforzato di espressioni (cioè {…; …}). - Senza ulteriori indugi:

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

Chiaramente abbiamo eseguito un incarico, usando =, al di fuori dei contesti (a) e (b). Quindi, perché la documentazione di una funzione di linguaggio R di base è stata errata per decenni?

È perché nella sintassi di R il simbolo =ha due significati distinti che si confondono abitualmente:

  1. Il primo significato è come operatore di assegnazione . Questo è tutto ciò di cui abbiamo parlato finora.
  2. Il secondo significato non è un operatore ma piuttosto un token di sintassi che segnala un argomento denominato che passa in una chiamata di funzione. A differenza =dell'operatore che non esegue alcuna azione in fase di runtime, cambia semplicemente il modo in cui un'espressione viene analizzata.

Vediamo.

In qualsiasi parte di codice del modulo generale ...

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

... il =è il segno che definisce il nome argomento passa: è non l'operatore di assegnazione. Inoltre, =è del tutto vietato in alcuni contesti sintattici:

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

Uno di questi genererà un errore "imprevisto" = "in ‹bla›".

In qualsiasi altro contesto, si =riferisce alla chiamata dell'operatore di assegnazione. In particolare, il semplice mettere tra parentesi la sottoespressione rende valido quanto sopra (a) e (b) un incarico . Ad esempio, il seguente esegue il compito:

median((x = 1 : 10))

Ma anche:

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

Ora potresti obiettare che tale codice è atroce (e potresti avere ragione). Ma ho preso questo codice dalla base::file.copyfunzione (sostituendolo <-con =) - è un modello pervasivo in gran parte della base di codice R di base.

La spiegazione originale di John Chambers , su cui probabilmente si basa la documentazione R, in realtà lo spiega correttamente:

[il =compito è] consentito solo in due punti della grammatica: al livello superiore (come programma completo o espressione digitata dall'utente); e quando isolato dalla struttura logica circostante, mediante parentesi graffe o una coppia extra di parentesi.


Una confessione: ho mentito prima. V'è una differenza ulteriore tra il =e <-operatori: che chiamano funzioni distinte. Per impostazione predefinita, queste funzioni fanno la stessa cosa ma è possibile sovrascriverle separatamente per modificare il comportamento. Al contrario, <-e ->(assegnazione da sinistra a destra), sebbene sintatticamente distinti, chiamano sempre la stessa funzione. Sovrascrivere l'uno prevale anche sull'altro. Sapere questo è raramente pratico ma può essere usato per alcuni divertenti shenanigans .


1
Riguardo alla precedenza e agli errori nel documento di R, la precedenza di ?è in realtà proprio nel mezzo =e <-, il che ha conseguenze importanti quando si esegue l'override ? , e praticamente nessuno altrimenti.
Moody_Mudskipper il

@Moody_Mudskipper è strano! Sembra che tu abbia ragione, ma secondo il codice sorgente ( main/gram.y), la precedenza di ?è documentata correttamente ed è inferiore a entrambe =e <-.
Konrad Rudolph,

Non parlo C ma suppongo che =ottenga un trattamento speciale prima della costruzione dell'albero di analisi. Forse in relazione agli argomenti della funzione, ha senso che foo(x = a ? b)cerchiamo =prima di analizzare il resto dell'espressione.
Moody_Mudskipper il


2
@Moody_Mudskipper FWIW questo è stato finalmente risolto in 4.0.0.
Konrad Rudolph,

661

La differenza negli operatori di assegnazione è più chiara quando li si utilizza per impostare un valore argomento in una chiamata di funzione. Per esempio:

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

In questo caso, xviene dichiarato nell'ambito della funzione, quindi non esiste nell'area di lavoro dell'utente.

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

In questo caso, xviene dichiarato nell'area di lavoro dell'utente, quindi è possibile utilizzarlo dopo che la chiamata di funzione è stata completata.


Esiste una preferenza generale tra la comunità R per l'utilizzo <-per l'assegnazione (tranne che per le firme di funzione) per la compatibilità con (molto) vecchie versioni di S-Plus. Si noti che gli spazi aiutano a chiarire situazioni come

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

La maggior parte degli IDE R ha scorciatoie da tastiera per <-facilitare la digitazione. Ctrl+ =in Architect, Alt+ -in RStudio ( Option+ -sotto macOS), Shift+ -(trattino basso) in emacs + ESS.


Se si preferisce la scrittura =a <-ma desidera utilizzare il simbolo di assegnazione più comune per il codice rilasciato pubblicamente (il CRAN, per esempio), allora si può utilizzare una delle tidy_*funzioni del formatRpacchetto di sostituire automaticamente =con <-.

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

La risposta alla domanda "Perché x <- y = 5genera un errore ma non x <- y <- 5?" è "Dipende dalla magia contenuta nel parser". La sintassi di R contiene molti casi ambigui che devono essere risolti in un modo o nell'altro. Il parser sceglie di risolvere i bit dell'espressione in diversi ordini a seconda che siano stati usati =o meno <-.

Per capire cosa sta succedendo, devi sapere che l'assegnazione restituisce silenziosamente il valore che è stato assegnato. Puoi vederlo più chiaramente stampando esplicitamente, per esempio print(x <- 2 + 3).

In secondo luogo, è più chiaro se utilizziamo la notazione con prefisso per l'assegnazione. Così

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

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

Il parser interpreta x <- y <- 5come

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

Potremmo aspettarci che x <- y = 5sarebbe così

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

ma in realtà viene interpretato come

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

Questo perché ha =una precedenza inferiore rispetto a <-, come mostrato nella ?Syntaxpagina di aiuto.


4
Questo è anche menzionato nel capitolo 8.2.26 di The R Inferno di Patrick Burns (Non io ma una raccomandazione comunque)
Uwe

3
Tuttavia, median((x = 1:10))ha lo stesso effetto di median(x <- 1:10).
Francesco Napolitano,

2
non li considero davvero scorciatoie, in ogni caso premi lo stesso numero di tasti
yosemite_k

5
Mi sono appena reso conto che la tua spiegazione di come x <- x = 5viene interpretata è leggermente sbagliata: in realtà, R la interpreta come ​`<-<-`(x, y = 5, value = 5)(che a sua volta è più o meno equivalente a tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5)). Yikes!
Konrad Rudolph,

4
... E ho appena capito che la prima parte di questa risposta è errata e, sfortunatamente, abbastanza fuorviante perché perpetua un malinteso comune: il modo in cui si utilizza =in una chiamata di funzione non esegue il compito e non è un operatore di compito. È un'espressione R analizzata completamente distinta, che usa semplicemente lo stesso carattere. Inoltre, il codice visualizzato non "dichiara" xnell'ambito della funzione. La dichiarazione di funzione esegue detta dichiarazione. La chiamata di funzione no (diventa un po 'più complicata con ...argomenti con nome ).
Konrad Rudolph,

103

La guida allo stile R di Google semplifica il problema vietando "=" per l'assegnazione. Non è una cattiva scelta.

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

Il manuale R fornisce dettagli dettagliati su tutti e 5 gli operatori di assegnazione.

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


133
Il rovescio della medaglia dell'assegnazione accidentale per x<-yquando x < -yera destinato, mi annoia tanto che preferisco personalmente =. Avere il tuo codice dipende dalla presenza di spazi bianchi non mi sembra buono. Va bene suggerire la spaziatura come consiglio di stile, ma per eseguire il codice in modo diverso indipendentemente dal fatto che ci sia uno spazio? Che cosa succede se si riformatta il codice o si utilizza la ricerca e la sostituzione, lo spazio bianco a volte può scomparire e il codice va storto. Non è un problema =. IIUC, proibire =equivale a richiedere " <- "; vale a dire, 3 caratteri incluso uno spazio, non solo " <-".
Matt Dowle,

12
Si noti che qualsiasi valore diverso da 0 è considerato TRUEda R. Quindi, se si intende verificare se xè inferiore a -y, è possibile scrivere if (x<-y)che non avviserà o si verificherà un errore e sembrerà funzionare correttamente. Sarà solo FALSEquando y=0, però.
Matt Dowle,

4
Se proibisci =e utilizzi <- , è difficile sostenere che grep "[^<]<-[^ ]" *.Rnon sia necessario un ulteriore passaggio . =non ha bisogno di un tale grep.
Matt Dowle,

34
Perché ferire gli occhi e le dita <-se puoi usarlo =? Nel 99,99% delle volte =va bene. A volte è necessario <<-però, che è una storia diversa.
Fernando,

10
L'attenzione su <- è forse uno dei motivi zoppi della mancanza di + = e - =.
Chris,

37

x = y = 5è equivalente a x = (y = 5), perché gli operatori di assegnazione "raggruppano" da destra a sinistra, che funziona. Significato: assegnare 5 a y, lasciando il numero 5; e quindi assegnare quel 5 a x.

Questo non è lo stesso (x = y) = 5, che non funziona! Significato: assegnare il valore di ya x, lasciando il valore di y; e quindi assegnare 5 a, umm ..., che cosa esattamente?

Quando mescoli i diversi tipi di operatori di assegnazione, i <-vincoli sono più stretti di =. Così x = y <- 5viene interpretato come x = (y <- 5), che è il caso che ha senso.

Sfortunatamente, x <- y = 5viene interpretato come (x <- y) = 5, che è il caso che non funziona!

Vedi ?Syntaxe ?assignOpsper le regole di precedenza (associazione) e di raggruppamento.


Sì, come ha detto la risposta di Konrad Rudolph<- <<- sopra = nella tabella delle precedenti, il che significa che <-verrà eseguita per prima. Quindi, x <- y = 5dovrebbe essere eseguito come (x <- y) = 5.
Nick Dong

1
@Nick Dong Sì, davvero. Utilmente, la tabella di precedenza dell'operatore è documentata in modo inequivocabile in ? Sintassi {base} .
Steve Pitchers,

33

Secondo John Chambers, l'operatore =è autorizzato solo al "livello superiore", il che significa che non è consentito nelle strutture di controllo come if, rendendo illegale il seguente errore di programmazione.

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

Mentre scrive, "Non consentire il nuovo modulo di assegnazione [=] nelle espressioni di controllo evita errori di programmazione (come nell'esempio sopra) che sono più probabili con l'operatore uguale che con altre assegnazioni S."

Puoi farlo se è "isolato dalla struttura logica circostante, da parentesi graffe o da una coppia di parentesi extra", quindi if ((x = 0)) 1 else xfunzionerebbe.

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


11
È un bug comune, x==0è quasi sempre inteso invece.
Aaron ha lasciato Stack Overflow il

14
Ah, sì, ho trascurato che hai detto "errore di programmazione". In realtà è una buona notizia che ciò causa un errore. E un buon motivo per preferire x=0come compito finito x<-0!
Steve Pitchers,

7
Sì, è bello che ciò provochi un errore, anche se traggo una lezione diversa su cosa preferire; Ho scelto di usare =il meno possibile perché =e ==aspetto così simile.
Aaron ha lasciato Stack Overflow il

2
Il modo in cui viene presentato questo esempio è così strano per me. if(x = 0) 1 else xgenera un errore, aiutandomi a trovare e correggere un bug. if(x <- 1) 1 else xnon genera un errore ed è molto confuso.
Gregor Thomas,

3
Voglio dire, un verificatore di errori davvero utile avrebbe gettato un errore lì e avrebbe detto "hai un codice inutile che restituirà sempre il elsevalore, intendevi scriverlo in quel modo?", Ma potrebbe essere un sogno irrealizzabile ...
TylerH,

26

Gli operatori <-e =assegnano nell'ambiente in cui vengono valutati. L'operatore <-può essere utilizzato ovunque, mentre l'operatore =è consentito solo al livello superiore (ad esempio, nell'espressione completa digitata al prompt dei comandi) o come una delle sottoespressioni in un elenco di espressioni rinforzate.


8
Penso che "livello superiore" significhi a livello di istruzione, piuttosto che a livello di espressione. Quindi di x <- 42per sé è una dichiarazione; in if (x <- 42) {}esso sarebbe un'espressione e non è valida. Per essere chiari, questo non ha nulla a che fare con il fatto che ci si trovi nell'ambiente globale o meno.
Steve Pitchers,

1
Questo: "l'operatore = è consentito solo ai massimi livelli" è un malinteso ampiamente ritenuto e completamente sbagliato.
Konrad Rudolph,

Questo non è vero - ad esempio, funziona, anche se il compito non è un'espressione completa:1 + (x = 2)
Pavel Minaev

1
Per chiarire i commenti di KonradRudolph e PavelMinaev, penso che sia troppo forte per dire che è completamente sbagliato, ma c'è un'eccezione, che è quando è "isolato dalla struttura logica circostante, da parentesi graffe o da un paio di parentesi extra".
Aaron lasciò Stack Overflow il

O in function() x = 1, repeat x = 1, if (TRUE) x = 1....
Moody_Mudskipper

6

Ciò può anche aggiungere alla comprensione della differenza tra questi due operatori:

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

Per il primo elemento R ha assegnato valori e nome proprio, mentre il nome del secondo elemento sembra un po 'strano.

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 versione 3.3.2 (2016-10-31); macOS Sierra 10.12.1


6
puoi dare una spiegazione più dettagliata del perché ciò accada / cosa sta succedendo qui? (suggerimento: data.frametenta di utilizzare il nome della variabile fornita come nome dell'elemento nel frame di dati)
Ben Bolker,

Ho pensato, potrebbe essere un bug? E se è così, come e dove posso segnalarlo?
Denis Rasulev,

7
non è un bug. Ho cercato di suggerire la risposta nel mio commento sopra. Quando si imposta il nome dell'elemento, R utilizzerà l'equivalente di make.names("b <- rnorm(10)").
Ben Bolker,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.