Quali sono le differenze tra “=” e “<-" in R?

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?

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

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

In questo caso, x è 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, x viene dichiarato nell'area di lavoro dell'utente, quindi è ansible utilizzarlo dopo che la chiamata alla funzione è stata completata.


C'è una preferenza generale tra la comunità R per l'utilizzo di <- per l'assegnazione (diversa dalle firme di funzioni) 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 rendere <- più facile da digitare. Ctrl + = in Architect, Alt + - in RStudio ( Opzione + - sotto macOS), Maiusc + - (trattino basso) in emacs + ESS.


Se si preferisce scrivere = a <- ma si desidera utilizzare il simbolo di assegnazione più comune per il codice rilasciato pubblicamente (ad esempio su CRAN), quindi è ansible utilizzare una delle funzioni tidy_* nel pacchetto formatR per sostituire automaticamente = con <- .

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

La risposta alla domanda "Perché x <- y = 5 lancia un errore ma non x <- y <- 5 ?" è "È la magia contenuta nel parser". La syntax 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 ordini diversi a seconda che sia stato utilizzato = o <- .

Per capire cosa sta succedendo, devi sapere che l'assegnazione restituisce silenziosamente il valore che è stato assegnato. Lo si può vedere più chiaramente stampando esplicitamente, ad esempio print(x <- 2 + 3) .

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

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

Il parser interpreta x <- y <- 5 come

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

Potremmo aspettarci che x <- y = 5 sarebbe allora

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

ma in realtà viene interpretato come

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

Questo perché = è una precedenza inferiore a <- , come mostrato nella pagina di aiuto della ?Syntax .

La guida di stile R di Google semplifica il problema vietando “=” per l’assegnazione. Non è una ctriggers scelta.

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

Il manuale R va in dettaglio su tutti e 5 gli operatori di assegnazione.

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

Secondo John Chambers, l’operatore = è consentito 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 

Come scrive, “Disabilitare 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 altri compiti di S”.

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

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

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

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

Quando mischi i diversi tipi di operatori di assegnazione, <- lega più stretto di = . Quindi x = y <- 5 è interpretato come x = (y <- 5) , che è il caso che ha senso.

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

Vedi ?Syntax e ?assignOps per la precedenza (associazione) e le regole di raggruppamento.

Gli operatori <- e = assegnano all'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.

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

Come mostra il tuo esempio, = e <- hanno una precedenza dell'operatore leggermente diversa (che determina l'ordine di valutazione quando sono mescolati nella stessa espressione). In effetti ?Syntax in 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?

Dal momento che stavi chiedendo agli operatori di assegnazione : sì, questa è l'unica differenza. Tuttavia, ti verrebbe perdonato per aver creduto diversamente. Anche la documentazione R di ?assignOps afferma che ci sono 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 troppo in risalto: la documentazione R è (sottilmente) sbagliata [ 1 ] . Questo è facile da mostrare: dobbiamo solo trovare un contro-esempio dell'operatore = che non è (a) al livello superiore, né (b) una sottoespressione in un elenco di espressioni rinforzate (cioè {…; …} ). - Senza ulteriori indugi:

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

Chiaramente abbiamo eseguito un compito, usando = , al di fuori dei contesti (a) e (b). Quindi, perché la documentazione di una caratteristica del linguaggio R è stata sbagliata per decenni?

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

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

Vediamo.

In qualsiasi pezzo di codice della forma generale ...

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

... the = è il token che definisce l'argomento named passing: non è l'operatore di assegnazione. Inoltre, = è completamente vietato in alcuni contesti sintattici:

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

Ognuno di questi genererà un errore "inaspettato" = "in ".

In qualsiasi altro contesto, = fa riferimento alla chiamata dell'operatore di assegnazione. In particolare, la semplice mera parentesi intorno alla sottoespressione rende uno qualsiasi dei punti (a) validi e (b) un compito . Ad esempio, il seguente incarico prevede:

 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 funzione base::file.copy (sostituendo <- with = ) - è un pattern pervasivo in gran parte del corebase R di base.

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

[ = assegnazione è] consentita in due soli posti nella grammatica: al livello superiore (come un programma completo o un'espressione digitata dall'utente); e quando isolato dalla struttura logica circostante, da parentesi graffe o da una coppia aggiuntiva di parentesi.


Una confessione: ho mentito prima. C'è un'ulteriore differenza tra gli operatori = e <- : chiamano funzioni distinte. Per impostazione predefinita, queste funzioni eseguono la stessa operazione ma è ansible ignorarle separatamente per modificare il comportamento. Al contrario, <- e -> (assegnazione da sinistra a destra), sebbene sintatticamente distinti, chiamano sempre la stessa funzione. Ignorando uno si sovrascrive anche l'altro. Sapere che questo è raramente pratico, ma può essere usato per alcuni divertenti scherzi .

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