domingo, 11 de março de 2012

Sobre a diferença entre os operadores de designação em R

Fiquei devendo a vocês uma explicação sobre a diferença entre os operadores <- e = na linguagem R. Os dois operadores designam o objeto nomeado à sua esquerda ao resultado da expressão à sua direita. Por exemplo, os comandos

> x <- 2+2

e

> x = 2+2

fazem a mesma coisa: designam ao valor 4 o objeto chamado "x". Lê-se "x recebe o resultado de dois mais dois" (em inglês, "x gets...").

Significa isso que os dois operadores funcionam exatamente da mesma maneira? Na verdade, não. Há uma diferença sutil entre eles.

Quem não quiser esquentar a cabeça com isso, não precisa ler o resto. Apenas siga esta recomendação: use <- para todas as designações, exceto quando estiver atribuindo valores a argumentos nomeados de funções. Por exemplo, escreva rep(1:3, each=2) e não rep(1:3, each<-2).

Estritamente falando, o símbolo da igualdade matemática não é adequado para representar a operação de designação. O motivo é que uma coisa não é igual ao seu nome. Existe uma hierarquia entre a coisa e o seu nome. Quando designamos um nome para alguma coisa, esta é mais importante. Uma mulher reagirá de uma maneira se o seu amante lhe entregar uma rosa; reagirá de outra maneira, se receber um pedaço de papel onde se lê a palavra "rosa". Além disso, a coisa, normalmente, precede o seu nome (embora o nome sobreviva à rosa, como Umberto Eco nos lembraria). Esses aspectos de hierarquia e precedência não existem numa igualdade matemática. A igualdade matemática é simétrica. Quando escrevo 2+2 = 4, não estou designando arbitrariamente o símbolo "4" à expressão 2+2. Estou afirmando uma equivalência entre a expressão e o quarto número inteiro positivo. Da mesma maneira, quando em matemática escrevo x = 2+2, não estou designando o símbolo "x" para a expressão à direita, mas afirmando que o valor da incógnita é igual ao valor da expressão. O que "x" representa não é a expressão 2+2, mas sim um valor desconhecido que é revelado pela expressão. A igualdade na matemática, em suma, tem a natureza de uma revelação. A designação, por outro lado, tem a natureza de uma imposição. Por isso, merece um símbolo diferente de "=". Numa linguagem de programação que usa = como operador de designação, x = y tem um significado diferente de y = x, o que é uma óbvia corrupção da notação matemática clássica. (Devo esse exemplo a Niklaus Wirth, veja o pós escrito abaixo.) 

Pelos motivos acima, algumas linguagens de programação dispõem de símbolos específicos para a designação de valores a variáveis. R é uma delas. Como vocês sabem, R é a versão livre da linguagem S de computação estatística. Quando S surgiu, em meados da década de 70, os seus criadores decidiram (provavelmente influenciados pela linguagem APL) que o símbolo  indicava de forma clara, sem ambiguidade, a natureza direcional e ativa da operação de designação: o nome à esquerda do operador é como um local para onde encaminhamos o resultado da expressão à direita do operador. A seta simboliza essa movimentação de um valor para uma posição na memória do computador, posição essa que passa a ser designada por um nome. Naquela época, nos laboratórios Bell, os pesquisadores utilizavam terminais Execuport através dos quais se podia digitar o símbolo    pressionando uma só tecla (essa tecla era "_" e é por isso que esse caractere, na linguagem S, também é um operador de designação; mas não em R). Quando S foi transportada para outros sistemas, adaptou-se o símbolo de designação para a atual combinação de um sinal de "menor que" seguido de um hífen: <-.

O fato, porém, é que as linguagens de programação mais populares hoje (C, Javascript, Python, etc.) usam o símbolo de igualdade como operador de designação. Essa prática é infeliz, mas é a realidade. Por isso, algumas pessoas, principalmente programadores que usam aquelas linguagens, preferem usar o símbolo de igualdade para designações. Atendendo a essa demanda, por volta do ano 2001 a linguagem S passou a admitir essa forma de designação. R herdou depois essa dupla notação, que permanece até hoje e às vezes causa alguma confusão.

Qual a diferença, afinal, entre os dois operadores? O operador <- funciona em qualquer lugar. O operador = só funciona na linha de comando, ou como uma das expressões numa lista agrupada de expressões entre chaves. Mas onde então ele não funcionaria? Eis um exemplo, para você digitar no console:

> x <- 1
> if (x = 1) 1 else 0
Erro: '=' inesperado em "if(x ="

Aqui o programador quer que o sistema verifique se o valor de x é igual a 1 e, em sendo, mostrar esse número; caso contrário, que mostre na tela o dígito zero. Mas R interpreta a expressão entre parênteses como uma designação imprópria, porque o operador = foi usado, e então acusa erro.

A maneira correta é usar o operador relacional ==, assim:

> x <- 1
> if (x == 1) 1 else 0
[1] 1

E quanto ao operador <-, como ele se comportaria nessa situação? Ele é mais forte do que o operador = e funciona dentro da estrutura de controle if. Experimente:

> x <- 0
> if (x <- 1) 1 else 0
[1] 1

Nesse caso, o que aconteceu foi o seguinte. O sistema executou a designação "forte" e impôs o valor 1 ao objeto x, que continha antes o valor 0. Um subproduto da designação é o próprio valor designado, que foi então coagido para o valor lógico TRUE (isto é, "verdadeiro"), levando finalmente ao resultado "1". Todo valor diferente de zero é coagido, nessa situação, para o valor lógico TRUE, por exemplo:

> x <- 0
> if (x <- 3.1415927) 1 else 0
[1] 1

No caso dos argumentos nomeados das funções, tradicionalmente usa-se o operador =, porque usar o operador mais forte <- faz a designação "transbordar" para fora do escopo da função. Por exemplo, a função raiz quadrada sqrt() tem um único argumento chamado "x", o qual não se costuma nomear explicitamente na chamada da função. Mas vamos nomeá-lo, para mostrar a diferença de escopo entre os operadores:

> x <- 0
> sqrt(x = 2)
[1] 1.414214
> x
[1] 0

Veja que o argumento da função (x = 2) não modificou o valor de x no ambiente global. Esse é normalmente o comportamento desejado, porque os nomes dos argumentos das funções são descartáveis, estamos interessados no resultado da função. Agora observe o que acontece se usamos o operador <- para designar o valor do argumento da função:

> x <- 0
> sqrt(x <- 2)
[1] 1.414214
> x
[1] 2

Agora a chamada da função modificou o conteúdo do objeto global x; o seu valor mudou de 0 para 2.

Essa diferença entre os operadores de designação em R é relevante? Para a maioria dos usuários, acredito que não. Por isso, alguns dizem que a escolha entre usar <- ou = acaba sendo uma questão de gosto. O símbolo de igualdade tem duas vantagens: é mais econômico (uma tecla ao invés de três) e aproxima o código R das linguagens mais populares. No nosso curso, porém, adotarei a recomendação que eu fiz lá em cima e repito aqui: usarei <- para todas as designações, exceto quando estiver atribuindo valores a argumentos nomeados de funções. Os motivos são os seguintes:

1. Como argumentei no início, designação não é igualdade. Por isso acho o uso do símbolo = uma impropriedade, mesmo que seja consagrado em outras linguagens.

2. Para quem é iniciante em programação, que é o caso da maioria dos alunos em Economia, o símbolo <- me parece o mais didático para indicar a designação.

3. Na grande maioria dos artigos, livros e blogs sobre R que vi até agora, os autores usam <- para designação. Parece, portanto, ser o padrão da linguagem.

Uma recomendação final: use sempre espaços ao redor de <-, para evitar a ambiguidade visual de um comando como x<-1 (estamos designando x ao valor 1 ou estamos testando se x é menor do que -1?).

PS: Para uma crítica ao uso do símbolo de igualdade na designação, vinda de um importante cientista da computação, veja a seção 4.1 do artigo "Good Ideas, Through the Looking Glass", de Niklaus Wirth, criador da linguagem Pascal.

Nenhum comentário:

Postar um comentário