Quais são as diferenças entre os operadores de atribuição "=" e "<-" no R?

712

Quais são as diferenças entre os operadores de atribuição =e <-em R?

Eu sei que os operadores são um pouco diferentes, pois este exemplo mostra

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

Mas essa é a única diferença?

csgillespie
fonte
45
Como observado aqui, as origens do <-símbolo vêm de teclados antigos da APL, que na verdade tinham uma única <-tecla.
joran

Respostas:

97

Quais são as diferenças entre os operadores de atribuição =e <-em R?

Como mostra seu exemplo, =e <-tem precedência de operador ligeiramente diferente (que determina a ordem da avaliação quando eles são misturados na mesma expressão). De fato, ?Syntaxem R fornece a seguinte tabela de precedência do operador, da mais alta para a mais baixa:

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

Mas essa é a única diferença?

Desde que você estava perguntando sobre os operadores de atribuição : sim, essa é a única diferença. No entanto, você seria perdoado por acreditar no contrário. Até a documentação R ?assignOpsafirma que existem mais diferenças:

O operador <-pode ser usado em qualquer lugar, enquanto o operador =é permitido apenas no nível superior (por exemplo, na expressão completa digitada no prompt de comando) ou como uma das subexpressões em uma lista entre expressões.

Não vamos enfatizar muito bem: a documentação do R está (sutilmente) errada [ 1 ] . Isso é fácil de mostrar: precisamos apenas encontrar um contra-exemplo do =operador que não esteja (a) no nível superior nem (b) uma subexpressão em uma lista de expressões (ie {…; …}). - Sem mais delongas:

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

Claramente, realizamos uma atribuição, usando =, fora dos contextos (a) e (b). Então, por que a documentação de um recurso central da linguagem R está errada há décadas?

É porque na sintaxe de R o símbolo =tem dois significados distintos que são rotineiramente confundidos:

  1. O primeiro significado é como um operador de atribuição . É disso que falamos até agora.
  2. O segundo significado não é um operador, mas um token de sintaxe que sinaliza o argumento chamado passando em uma chamada de função. Ao contrário do =operador, ele não executa nenhuma ação no tempo de execução, apenas altera a maneira como uma expressão é analisada.

Vamos ver.

Em qualquer parte do código da forma geral…

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

... o =é o sinal que define chamado argumento passando: ele é não o operador de atribuição. Além disso, =é totalmente proibido em alguns contextos sintáticos:

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

Qualquer uma dessas opções gerará um erro “inesperado '=' em ‹bla›”.

Em qualquer outro contexto, =refere-se à chamada do operador de atribuição. Em particular, apenas colocar parênteses em torno da subexpressão torna qualquer uma das alternativas (a) acima e (b) uma atribuição . Por exemplo, o seguinte executa a atribuição:

median((x = 1 : 10))

Mas também:

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

Agora você pode objetar que esse código é atroz (e você pode estar certo). Mas peguei esse código da base::file.copyfunção (substituindo <-por =) - é um padrão generalizado em grande parte da base de código R principal.

A explicação original de John Chambers , na qual a documentação do R provavelmente está baseada, na verdade explica isso corretamente:

[a =atribuição é] permitida em apenas dois lugares da gramática: no nível superior (como um programa completo ou expressão digitada pelo usuário); e quando isolado da estrutura lógica circundante, por chaves ou um par extra de parênteses.


Uma confissão: eu menti antes. Não é uma diferença adicional entre a =e <-operadores: eles chamam funções distintas. Por padrão, essas funções fazem a mesma coisa, mas você pode substituí-las separadamente para alterar o comportamento. Por outro lado, <-e ->(atribuição da esquerda para a direita), embora sintaticamente distintos, sempre chamam a mesma função. Substituir um também substitui o outro. Saber isso raramente é prático, mas pode ser usado para algumas travessuras divertidas .

Konrad Rudolph
fonte
1
Sobre a precedência e os erros no documento de R, a precedência de ?realmente está no meio =e <-, o que tem consequências importantes ao substituir ? , e praticamente nenhuma outra.
Moody_Mudskipper
@Moody_Mudskipper isso é bizarro! Você parece estar certo, mas de acordo com o código fonte ( main/gram.y), a precedência de ?está documentada corretamente e é menor que ambos =e <-.
Konrad Rudolph
Eu não falo C, mas acho que =receba um tratamento especial antes que a árvore de análise seja construída. Talvez relacionado a argumentos de função, faz sentido que foo(x = a ? b)procuraríamos =antes de analisar o restante da expressão.
Moody_Mudskipper
@Moody_Mudskipper Eu pedi r-devel
Konrad Rudolph
2
@Moody_Mudskipper FWIW, isso finalmente foi corrigido na 4.0.0.
Konrad Rudolph
661

A diferença nos operadores de atribuição é mais clara quando você os utiliza para definir um valor de argumento em uma chamada de função. Por exemplo:

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

Nesse caso, xé declarado no escopo da função, portanto, não existe no espaço de trabalho do usuário.

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

Nesse caso, xé declarado no espaço de trabalho do usuário, para que você possa usá-lo após a conclusão da chamada da função.


Existe uma preferência geral entre a comunidade R pelo uso <-para atribuição (exceto nas assinaturas de funções) para compatibilidade com (muito) versões antigas do S-Plus. Observe que os espaços ajudam a esclarecer situações como

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

A maioria dos IDEs R possui atalhos de teclado para <-facilitar a digitação. Ctrl+ =no Architect, Alt+ -no RStudio ( Option+ -no macOS), Shift+ -(sublinhado) no emacs + ESS.


Se preferir escrever =a <-mas quiser usar o símbolo de atribuição mais comum para o código lançado publicamente (em CRAN, por exemplo), então você pode usar uma das tidy_*funções no formatRpacote para substituir automaticamente =com <-.

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

A resposta para a pergunta "Por que x <- y = 5lançar um erro, mas não x <- y <- 5?" é "Tudo depende da magia contida no analisador". A sintaxe de R contém muitos casos ambíguos que precisam ser resolvidos de uma maneira ou de outra. O analisador escolhe resolver os bits da expressão em diferentes ordens, dependendo de se foi =ou <-não usado.

Para entender o que está acontecendo, você precisa saber que a atribuição silenciosamente retorna o valor que foi designado. Você pode ver isso mais claramente imprimindo explicitamente, por exemplo print(x <- 2 + 3).

Em segundo lugar, fica mais claro se usarmos notação de prefixo para atribuição. assim

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

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

O analisador interpreta x <- y <- 5como

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

Podemos esperar que x <- y = 5seria então

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

mas na verdade é interpretado como

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

Isso ocorre porque a =precedência é menor do que <-, conforme mostrado na ?Syntaxpágina de ajuda.

Richie Cotton
fonte
4
Isso também é mencionado no capítulo 8.2.26 do A R Inferno por Patrick Burns (Not me, mas um de qualquer maneira recomendação)
Uwe
3
No entanto, median((x = 1:10))tem o mesmo efeito que median(x <- 1:10).
Francesco Napolitano
2
eu realmente não considerá-los atalhos, em qualquer caso, você pressiona mesmo número de chaves
yosemite_k
5
Acabei de perceber que sua explicação de como x <- x = 5é interpretada está um pouco errada: Na realidade, R a interpreta como ​`<-<-`(x, y = 5, value = 5)(o que em si é mais ou menos equivalente a tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5)). Caramba!
27518 Konrad Rudolph
4
… E acabei de perceber que a primeira parte desta resposta está incorreta e, infelizmente, bastante enganadora, porque perpetua um equívoco comum: a maneira como você usa =em uma chamada de função não realiza atribuição e não é um operador de atribuição. É uma expressão R analisada totalmente distinta, que por acaso usa o mesmo caractere. Além disso, o código que você mostra não “declara” xno escopo da função. A declaração da função executa a referida declaração. A chamada de função não (fica um pouco mais complicada com ...argumentos nomeados ).
Konrad Rudolph
103

O guia de estilo R do Google simplifica o problema ao proibir o "=" para atribuição. Não é uma má escolha.

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

O manual R entra em detalhes interessantes em todos os 5 operadores de atribuição.

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

Nosredna
fonte
133
A desvantagem da atribuição acidental de x<-yquando x < -yfoi feita me irrita tanto que eu pessoalmente prefiro =. Ter seu código depende da presença de espaço em branco não me parece bom. Não há problema em sugerir espaçamento como orientação de estilo, mas para que seu código seja executado de maneira diferente, independentemente de haver um espaço ou não? E se você reformatar seu código ou usar a pesquisa e a substituição, o espaço em branco às vezes pode desaparecer e o código dá errado. Isso não é um problema =. IIUC, proibir =equivale a exigir " <- "; ou seja, 3 caracteres, incluindo um espaço, não apenas " <-".
precisa
12
Observe que qualquer não-0 é considerado TRUEpor R. Portanto, se você pretende testar se xé menor que -y, pode escrever o if (x<-y)que não avisa ou erro e parece funcionar bem. Só será FALSEquando y=0.
precisa
4
Se você proíbe =e usa <- , é difícil argumentar que uma etapa extra grep "[^<]<-[^ ]" *.Rnão é necessária. =não precisa de tal grep.
precisa
34
Por que machucar seus olhos e dedos <-se você pode usar =? Em 99,99% das vezes =é bom. Às vezes você precisa <<-, porém, que é uma história diferente.
Fernando
10
O foco em <- é talvez uma das razões esfarrapadas para a falta de + = e - =.
Chris
37

x = y = 5é equivalente a x = (y = 5), porque os operadores de atribuição "agrupam" da direita para a esquerda, o que funciona. Significado: atribua 5 a y, deixando o número 5; e depois atribua esse 5 a x.

Não é o mesmo que (x = y) = 5, o que não funciona! Significado: atribua o valor de ya x, deixando o valor de y; e depois atribui 5 a, umm ..., o que exatamente?

Quando você mistura os diferentes tipos de operadores de atribuição, as <-ligações são mais apertadas que =. Então, x = y <- 5é interpretado como x = (y <- 5), que é o caso que faz sentido.

Infelizmente, x <- y = 5é interpretado como (x <- y) = 5, que é o caso que não funciona!

Consulte ?Syntaxe ?assignOpspara as regras de precedência (encadernação) e agrupamento.

Steve Pitchers
fonte
Sim, como a resposta de Konrad Rudolph disse <- <<-está acima = na tabela de precedência, o que significa que <-será realizado primeiro. Então, x <- y = 5deve ser executado como (x <- y) = 5.
Nick Dong
1
@ Nick Dong Sim, de fato. Felizmente, a tabela de precedência do operador está documentada sem ambiguidade na ? Sintaxe {base} .
Steve jarros
33

De acordo com John Chambers, o operador =é permitido apenas no "nível superior", o que significa que não é permitido em estruturas de controle como if, tornando ilegal o seguinte erro de programação.

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

Como ele escreve, "A exclusão do novo formulário de atribuição [=] nas expressões de controle evita erros de programação (como o exemplo acima) que são mais prováveis ​​com o operador igual do que com outras atribuições S."

Você pode fazer isso se estiver "isolado da estrutura lógica circundante, por chaves ou um par extra de parênteses", assim if ((x = 0)) 1 else xfuncionaria.

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

Aaron deixou Stack Overflow
fonte
11
É um bug comum, x==0quase sempre significa isso.
Aaron saiu de Stack Overflow
14
Ah, sim, eu esqueci que você disse "erro de programação". Na verdade, é uma boa notícia que isso causa um erro. E um bom motivo para preferir a x=0atribuição x<-0!
Steve Pitchers
7
Sim, é bom que isso cause um erro, embora eu tire uma lição diferente sobre o que preferir; Eu escolho usar =o mínimo possível porque =e ==pareço tão semelhante.
Aaron saiu de Stack Overflow
2
A maneira como este exemplo é apresentado é tão estranha para mim. if(x = 0) 1 else xgera um erro, ajudando-me a encontrar e corrigir um erro. if(x <- 1) 1 else xnão gera um erro e é muito confuso.
Gregor Thomas
3
Quero dizer, um verificador de erros realmente útil lançaria um erro e diria "você tem um código inútil que sempre retornará o elsevalor, você quis escrever dessa maneira?", Mas isso pode ser um sonho ...
TylerH 8/03
26

Os operadores <-e =atribuem ao ambiente em que são avaliados. O operador <-pode ser usado em qualquer lugar, enquanto o operador =é permitido apenas no nível superior (por exemplo, na expressão completa digitada no prompt de comando) ou como uma das subexpressões em uma lista entre expressões.

Haim Evgi
fonte
8
Eu acho que "nível superior" significa no nível da declaração, e não no nível da expressão. Então, x <- 42por si só, é uma afirmação; no if (x <- 42) {}que seria uma expressão, e não é válido. Para ser claro, isso não tem nada a ver com você estar no ambiente global ou não.
Steve jarros
1
O seguinte: “o operador = só é permitido no nível superior” é um mal-entendido amplamente aceito e completamente errado.
Konrad Rudolph
Isso não é verdade - por exemplo, isso funciona, mesmo que a atribuição não seja uma expressão completa:1 + (x = 2)
Pavel Minaev 5/17/17
1
Para esclarecer os comentários de KonradRudolph e PavelMinaev, acho que é muito forte dizer que está completamente errado, mas há uma exceção, que é quando é "isolado da estrutura lógica circundante, por chaves ou um par extra de parênteses".
Aaron deixou Stack Overflow
Ou function() x = 1, repeat x = 1, if (TRUE) x = 1....
Moody_Mudskipper
6

Isso também pode aumentar a compreensão da diferença entre esses dois operadores:

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

Para o primeiro elemento, R atribuiu valores e nome próprio, enquanto o nome do segundo elemento parece um pouco estranho.

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

Denis Rasulev
fonte
6
você pode dar uma explicação mais detalhada sobre por que isso acontece / o que está acontecendo aqui? (dica: data.frametenta utilizar o nome da variável fornecida como o nome do elemento na trama de dados)
Ben Bolker
Apenas pensei, isso poderia ser um bug? E se sim, como e onde eu denuncio?
Denis Rasulev 15/07
7
não é um bug. Tentei sugerir a resposta no meu comentário acima. Ao definir o nome do elemento, R usará o equivalente a make.names("b <- rnorm(10)").
precisa