2 Sintaxe e Variáveis
Introdução
Neste capítulo, vamos aprender, principalmente, a estrutura de um programa em R, e os tijolinhos que a compõe.
Haverão dois aprendizados principais: (i) a estrutura pode ser entendida como a de um texto instrucional; (ii) temos apenas dois tijolos, (nomes de) objetos e (chamadas de) funções; (iii) como é a relação entre variáveis e a memória do computador. Esses tópicos principais são bastante teóricos/abstratos, mas serão úteis no futuro, e seu entendimento gera a habilidade de generalizar esses conceitos para outras linguagens de programação.
Aprenderemos também tópicos mais concretos: (i) a sintaxe básica da linguagem; (ii) como definir e como funcionam as variáveis; (iii) as funções (operadores) básicas da linguagem.
2.1 Sintaxe e Tijolinhos
Em termos simplistas, um script é um arquivo de texto com instruções a serem executadas por um computador.
Note que o computador, no nível mais baixo, apenas entende sequências de 0s e 1s, de modo que um script precisa ser “traduzido”. Algumas linguagens são compiladas, onde o texto inteiro é traduzido antes de serem executado, outras são interpretadas, traduzidas “na hora”, frase a frase. O R é o segundo caso, por isso que conseguiremos rodar códigos linha a linha – em vez de apenas poder executar um arquivo inteiro.
Um script é um texto instrucional como qualquer outro, escrito em um tipo de linguagem especial, “de programação”, mas é um texto. Similar à receita de bolo de cenoura da minha vó, ou o roteiro da peça escolar onde interpretei, com maestria, a árvore #3.
Como em qualquer linguagem, temos um vocabulário à disposição, um conjunto de palavras (ou tokens) existentes, organizadas em categorias como substantivos, verbos, etc. Nós combinamos as palavras em frases (ou statements/declarações) para descrever as instruções. Por fim, podemos opcionalmente organizar o texto em parágrafos (ou blocks), conjuntos de frases que devem ser lidas juntas, para definir a estrutura e facilitar o entendimento do texto.
Ok, a receita da minha vó não tinha parágrafos, a metáfora não é perfeita, paciência.
A seguir, vamos descrever com mais calma esses conceitos de tokens, statements e blocks.
2.1.1 Objetos e Funções
Existem dois tijolinhos principais no R:
Objetos
- valores (dados), que podem carregar também atributos (metadados).
- Cada valor tem um tipo/type36.
- Podem ser associados à um nome/symbol. O conjunto nome-objeto é chamado de variável. Mas também podem ser escritos “por extenso”, como
1ou"Hello World!".
em ciência da computação, o termo “objeto” é relacionado à Programação Orientada ao Objeto, significando “uma instância de uma classe”. Esse conceito irá existir no R, mas não é o que estamos tratando aqui. Aqui, “objeto” é um termo mais genérico.
Funções
- Definem operações à serem realizadas usando objetos.
-
Operadores, símbolos como
+e>, são uma categoria de função especial, que apresentam uma sintaxe mais enxuta.
Note que descrevemos tudo que há na linguagem em apenas dois conceitos, objetos e funções. Em outras palavras:
“[In R,] everything that exists is an object; everything that happens is a function call.” — John Chambers.
É esse tipo de simplicidade que, na minha opinião, torna aprender a “teoria” sobre o R tão interessante:
- Veremos que ter essa categoria de conhecimento sobre a linguagem facilitará muito o entendimento de temas futuros.
- Estaremos adquirindo a habilidade de aprender o funcionamento de linguagens de programação no geral, usando um exemplo (R) simples.
O capítulo 3 explicará os objetos, e 6 as funções. Pela lógica, esses dois devem conter a completude do conhecimento sobre R, e o resto do livro é só enfeite.
2.1.2 Palavras
Voltando à nossa analogia, tudo que você lê em um script é uma de duas (três) coisas.
Vocabulário
Quais palavras/tokens, temos disponíveis na linguagem R?
- Substantivos, os nomes de objetos (ou objetos escritos por extenso).
- Verbos, as (chamadas de) funções.
Para ser preciso, também tempos alguns caracteres puramente estéticos, syntactic sugar, como espaços e tabs.
Mais para frente, veremos que podemos salvar excertos de código R em um objeto, e teremos apenas três tipos de objetos da linguagem.
Mas vamos sair da abstração. Como escrevemos essas palavras em um texto de R?
- Valores:
- Strings: utilize aspas duplas
"ou aspas simples':"Hello World!",'Hello World!'. A galerinha do R costuma usar mais as duplas.. - Números: simplesmente escreva-os:
1. Use um ponto.como o separador decimal0.01. - Booleanos: escreva as palavras especiais
TRUEeFALSE.
- Strings: utilize aspas duplas
- Variáveis: o operador
<-associa o nome à esquerda ao valor à direita:a <- 1. Não se preocupe muito com isso por agora. - Funções: escreva o nome da função, e os argumentos que ela receberá entre parênteses
sum(1, 1). Não se preocupe muito com elas por agora.- Operadores: são imputados como
1 + 1. - Parênteses podem ser utilizados em operações múltiplas:
(1+1)/2.
- Operadores: são imputados como
-
Comentários: texto que não será avaliado como código. Use o símbolo
#, que torna tudo após dele, na mesma linha, um comentário.
2.1.3 Frases
Como dito, as frases no R serão combinações de tokens. Qualquer combinação. Podem ser apenas um valor (1), o nome de uma variável (a), ou a chamada de uma função (sum(1, 1)).
cada linha do código abaixo é uma frase:
E quanto a um código como 1 +? Bom, esse código não é sintaticamente correto. Normalmente, chamamos de statement/declaração apenas as frases sintaticamente corretas, e eu irei passar a usar essa nomenclatura em diante.
2.1.4 Parágrafos
Um mesmo texto pode ser organizado de várias maneiras diferentes, e isso é verdade para um script também. Como delimitamos as declarações?
- Por padrão, uma declaração acaba na quebra de linha.
- Se a declaração terminar inacabada (como no exemplo
1 +), o R ignora a quebra de linha, e tenta completar a declaração com a linha seguinte. - Um ponto e vírgula pode ser usado para delimitar uma declarações explicitamente:
1 + 1; 2 + 2. Mas seu uso não é uma comum nem recomendável.
- Se a declaração terminar inacabada (como no exemplo
- Várias declarações podem ser agregadas em grupos ou blocks usando chaves
{}.
um grupo de declarações é uma declaração em si. As chaves são uma função, que combina várias declarações em uma só. Uou, tudo realmente é uma chamada de função.
Note que, assim como em um texto, a organização em parágrafos não necessariamente afeta a lógica do texto, o que o script faz, pode afetar apenas sua organização.
Antes de avançar, vou mentir um pouco e dar duas definições, mas que serão melhor detalhadas no futuro.
Expressão
É uma declaração não avaliada, “congelada”, que o R não rodou. Elas terão seu próprio tipo de objeto.
Ter um tipo de objeto para isso é uma característica definidora do R, usaremos padrões de programação que operam sobre a própria linguagem.
Função
É uma expressão, que depende de variáveis, possivelmente associada à um nome. Utilizar esse nome, indicando os valores das variáveis envolvidas (entre parênteses ()), avalia a declaração, retornando seu resultado. Elas terão seus próprios tipos de objeto (mais de um).
Veja o exemplo abaixo, mas não se preocupe, irei detalhar mais sobre funções no capítulo 6. Mas note como salvamos uma função da mesma maneira que salvamos qualquer outro objeto.
liar <- function(x, y) {x + y}
liar(x = 1, y = 2) #> 3
liar(1, 2) #equivalente à declaração anteriorCom essa definição em mente, na nossa analogia do começo do capítulo, funções são “parágrafos nomeáveis”.
2.2 Variáveis
As variáveis merecem mais da nossa atenção. Mas não é para elas se acharem demais, valores/atributos e funções terão seus próprios capítulos.
2.2.1 Definindo Variáveis
Para definir variáveis, escrevemos seu nome, = ou <-, e a declaração que definirá seu valor e atributos: x = 1 + 1, x <- 1 + 1.
Também podemos usar a função assign(), mas que será explicada no futuro.
Ao rodar algo como x <- 1, o objeto 1 é salvo na memória do computador, e associado ao nome x. Como dito anteriormente, a variável é o conjunto objeto-nome.
O diagrama abaixo, inspirado no presente em Advanced R, ilustra esse processo. Note que o objeto existe na memória do computador, em uma localidade específica, e o nome é apenas uma referência à ele.
O objeto pode ser pensado como a localidade na memória em si, ou o que reside nela. Portanto, no diagrama, o objeto tem uma etiqueta, com o seu endereço de memória37.
2.2.2 Copy on Modify
2.2.2.1 Criando Referências Repetidas
Com base no que aprendeu, o que acontece ao rodar y <- x? Temos duas opções:
-
ypode ser um novo nome, associado ao mesmo objeto – mesma posição na memória do computador – quex.
-
ypode ser um novo nome, associado a um novo objeto – nova posição na memória do computador – quex, mas carregando a mesma informação.
Note que sempre temos duas variáveis, pois são dois pares nome-objetos diferentes.
O que acontece no R é o primeiro caso. Isso evita que se separe um novo lugar na memória quando não se é necessário, economizando espaço e tempo.
2.2.2.2 Modificando Referências Repetidas
Agora, o que acontece com x quando modificamos y, ou vice versa? Por exemplo, y <- 2? Novamente, temos duas opções:
- O objeto associado à
yé modificado diretamente em seu local da memória, de modo quextambém “verá” a mudança.
- O objeto é antes copiado para um novo local de memória, para então ser modificado apenas lá, de modo que
xnão “veja” a mudança.
O nome do comportamento no caso 1 é modify-in-place.
No R, o que acontece é o caso 2. Esse processo, de apenas “separar”/“copiar” objetos quando modificados, é chamado de copy-on-modify. Também falamos que os objetos no R, fora as exceções descritas abaixo, são imutáveis.
Copy-on-modify
No R, um mesmo objeto pode ter ser referenciado a mais de um nome, gerando várias variáveis. Modificar alguma delas não modifica o objeto diretamente, mas copia-o em um novo local na memória, para abrigar o objeto modificado. Isso é, os objetos são copiados-após-mudanças.
Existem duas exceções: objetos com apenas uma referência/um nome, e ambientes38. Esses são alterados “na hora”/“no lugar”, ou modify-in-place.
A melhor maneira de ver quando um objeto é copiado é olhando empiricamente. Veja a função tracemem() na seção 2.3.1 do Advanced R.
2.2.2.3 Especificidades
Diferentes tipos de valores terão diferentes relações com esse processo, mas vou poupá-los disso. O importante é saber que, na maioria dos casos, alterar y não altera x e vice versa, independente da complexidade do objeto.
se você está familiar com o conceito de lista, veja como elas se comportam em Advanced R seção 2.3.3. Em termos simplistas, listas são coleções de referências a objetos, e não coleções de objetos (diretamente).
este tema é complexo, e foi bastante simplificado. Vide Advanced R, seções “2.3 - Copy-on-modify” e “2.5 - Modify-in-place”. Os exemplos utilizando a função tracemem() são especialmente úteis.
2.2.3 Outras Características
Note que = e <- são muito similares, mas = serve para mais coisas que somente definição de variáveis, como indicar argumentos em uma função. Portanto, <- funciona como “definidor” em mais contextos, e é uma má prática utilizar = como definidor.
Ambos podem definir várias variáveis de uma vez: x = y = 3, x <- y <- 3, x = y <- 3.
Podemos usar também -> da esquerda pra direita: 3 -> x.
Existe um terceiro operador <<-, que será discutido no capítulo 6.
2.2.4 Regras de Nomenclatura
Nem toda combinação de caracteres pode ser um nome de variável. As principais regras são:
- Nomes podem conter letras, números, “.” e “_“.
- São case-sensitive.
- Podem começar apenas com letras ou “.”.
- Não podem ser palavras reservadas como “TRUE”.
Nomes não sintáticos podem ser definidos, se escritos usando crases (backticks): `_x` <- 1. Você provavelmente encontrará isso ao importar dados que não foram criados no R.
em muitos momentos, o R converte nomes não sintáticos utilizando a função make.names(). Você aprenderá sobre ela nos exercícios. É muito importante estar atento à esse comportamento, uma vez que é causa comum de erros.
Você verá que isso é um tema comum: o R tenta facilitar muitas tarefas, fazendo as coisas por você. Isso por um lado é o que o torna fácil de sair trabalhando, mas é causa de inconsistências.
2.3 Operadores
A princípio, deixaria os detalhes sobre operadores para os exercícios, mas fiquei com medo deles se sentirem excluídos.
Abaixo estão os operadores relevantes para o momento, suas descrições, e seu uso. Clique nos links dos operadores para abrir suas páginas de ajuda.
| Categoria | Operador | Descrição | Uso |
|---|---|---|---|
| aritmétrica | + |
soma | num + num |
| aritmétrica | - |
subtração | num - num |
| aritmétrica | * |
multiplicação | num * num |
| aritmétrica | / |
divisão | num / num |
| aritmétrica | ^ |
exponenciação | num ^ num |
| aritmétrica | %% |
divisão inteira | num %% num |
| aritmétrica | %/% |
resto da divisão | num %/% num |
| comparação | == |
igual | x == y |
| comparação | != |
diferente | x != y |
| comparação | \< |
menor que | num < num |
| comparação | > |
maior que | num > num |
| comparação | >= |
maior igual | num >= num |
| comparação | <= |
menor igual | num <= num |
| lógica | ! |
“não” lógico | ! logi |
| lógica | & |
“e” lógico | logi & logi |
| lógica | │ |
“ou” lógico | logi │ logi |
| agrupadores | { |
agrupador chaves | { expr } |
| agrupadores | ( |
agrupador parênteses | ( expr ) |
na coluna de “Uso”, “logi” se refere a qualquer valor que se comporte como um valor booleano, “num” a qualquer valor que se comporte como número, “expr” à qualquer statement, e “x”/“y” à valores mais genéricos, ou à nomes de variáveis.
Agora vou apresentar a ordem de precedência da aplicação dos operadores. Associatividade se refere à como são resolvidos “empates”, “direita pra esquerda” significa que o operador mais à direita é analisado antes. Tudo ficará mais claro no exemplo que segue.
| Operador | Descição | Associatividade |
|---|---|---|
(), {}
|
agrupadores | dir. → esq. |
::, :::
|
acessar namespaces (*) | dir. → esq. |
$, @
|
extração de components/slots (*) | esq. → dir. |
[, [[
|
indexação (*) | esq. → dir. |
^ |
exponenciação | esq. → dir. |
-, + (unário) |
mais e menos unários | esq. → dir. |
: |
sequências (*) | esq. → dir. |
%%, %/%, %xyz%, │>
|
operadores especiais (*) | esq. → dir. |
*, /
|
multiplicação, divisão | esq. → dir. |
+, - (binário) |
adição, subtração | esq. → dir. |
>, >=, <, <=, ==, !=
|
comparações | esq. → dir. |
! |
“não” lógico | esq. → dir. |
&, &&
|
“e” lógico (*) | esq. → dir. |
│, ││
|
“ou” lógico (*) | esq. → dir. |
~ (unário e binário) |
fórmula (*) | esq. → dir. |
->, ->>
|
definição pra direita (*) | esq. → dir. |
<-, <<-
|
definição pra esquerda (*) | esq. → dir. |
= (definidor) |
definição pra esquerda | dir. → esq. |
linhas com um “(*)” apresentam operadores que ainda não foram apresentados.
para deixar mais claro, na declaração (3+6)/(1+2)/5^4 o seguinte ocorre:
-
()são analisados primeiro, porque tem precedência (estão acima na tabela).- Como é da direita pra esquerda, primeiro
(1+2)vira3, e depois,(3+6)vira9.
- Como é da direita pra esquerda, primeiro
-
^é analisado a seguir, logo,5^4vira625. -
/é analisado a seguir.- Como é da esquerda pra direita, primeiro
9/3vira3, e depois3/625vira0.0048.
- Como é da esquerda pra direita, primeiro
Vide Working With R, capítulo “6 - Basic Operations” para mais detalhes e exemplos. Cuidado, alguns conceitos utilizados lá não foram vistos ainda.
Complemento
Recapitulando
Sintaxe
Neste capítulo, vimos a estrutura geral de um script:
- Temos palavras/tokens, frases/statements/declarações, e parágrafos/blocks.
- No R, tudo que existe é um objeto, e tudo que acontece é uma chamada de função.
- Objetos serão o tema do próximo capítulo, e funções, do capítulo 6.
- Vimos como imputar cada token.
- As declarações são combinações de tokens.
- Costumam ser delimitadas por quebras de linha.
- Declarações podem ser organizadas em parágrafos vias chaves.
- Scripts são sequências de declarações.
Vimos definições inciais para dois conceitos importantes:
- Uma expressão é uma declaração ainda não avaliada.
- Uma função é uma expressão, que depende de variáveis, associada à um nome.
- Funções podem ser entendidas como “parágrafos nomeáveis”.
Variáveis
Também demos uma atenção extra ao conceito de variável:
- Variáveis são um objeto com um nome associado.
- Aprendemos os operadores que definem variáveis
=e<-, e porque<-é preferível. - Vimos características como
x <- y <- 3e as regras de nomenclatura.
Bem como alguns conceitos mais técnicos e avançados:
- A dinâmica da memória no R é pautada pelo conceito de copy-on-modify. No R, um mesmo objeto pode ter ser referenciado a mais de um nome. Modificar algum deles não modifica o objeto original, e sim copia-o, criando um novo objeto. Isso é, os objetos são copiados-após-mudanças.
- As exceções são objetos com apenas uma referência, e ambientes, que usam modify-in-place (são modificados “no lugar”).
Dicionário de Funções
Abaixo segue a lista de funções vistas neste capítulo.
| Categoria | Função | Descrição |
|---|---|---|
| operadores |
+, -, *, /, \^, %%, %/%
|
aritimétrica |
| operadores |
==, !=, <, >, >=, <=
|
comparação |
| operadores |
!, &, │
|
lógica |
| operadores |
{, (
|
organização de expressões |
| operadores |
<-, ->, =,
|
definição de variáveis |
| variáveis |
assign(), remove(), rm()
|
definição e remoção de variáveis |
| variáveis |
make.names()
|
criar nomes sintáticos |
| ajuda |
help(), vignette(), ?
|
ajuda sobre funções e pacotes |
Referências
As referências principais deste capítulo são:
- “Advanced R” capítulo “2 - Names and Values”, excluindo as seções 2.4 e 2.6.
- “R Language” seções “2.1.3 - Language Objects” e “2.1.3 - Expression Objects”.
- “R Introduction” seções “2.1 - Vectors and Assignment” e “2.2 - Vector Arithmetic”.
- “R Internals” seção “1.1 - SEXPs”.
- “R Help” os documentos de ajuda das funções aqui expostas.
para outros temas não contidos aqui, mas fortemente relacionados com esse capítulo, vide Advanced R seções “2.4 - Object Size” e “2.6 - Unbinding and the Garbage Collector”.
Exercícios
os exercícios usam variáveis de mesmo nome. Garanta que você está utilizando as definições corretas. É necessário ver as páginas de ajuda das funções. Rode ?nome_na_funcao no console.
Variáveis
- Explique a diferença entre o objeto
1e cada uma das variáveis abaixo.
1
a <- 1
b <- a
c <- a + 1
d <- b
e <- 1- Foi comentado que objetos mais complexos têm comportamentos diferentes com relação ao processo de definição , mas todos seguem uma característica geral. Com base nisso, o que você espera que ocorra com
bapós a terceira linha de cada um dos código abaixo? Justifique, fazendo referência à essa tal característica geral.
a função list() cria uma lista, que é uma coleção de valores. O operador [[ seleciona um elemento dela. Aprenderemos esses conceitos nos capítulos seguintes.
- Leia a página de ajuda da função
make.names()para aprender as regras completas de definição de nomes, sobre como o R converte nomes não sintáticos. Preveja qual será o resultado das chamadas abaixo.
make.names("")
make.names("@")
make.names("TRUE")
make.names("`TRUE`")
make.names(c("a", "a", "a", "b", "b"), unique = TRUE)Operadores
- Parta de um número qualquer
x, por exemplo,x <- 5. Use os ensinados operadores para criar:
- Uma frase que retorne
TRUEsexfor múltiplo de 2. - Uma frase que retorne
TRUEsexnão for múltiplo nem de 3, nem de 5. - Uma frase que retorne
TRUEse a parte inteira da divisão dexpor 4 é igual a 2, ou se seu quadrado for maior ou igual à 10.
- Liste a ordem das ações executadas no cálculo da expressão abaixo:
x <- FALSE | !5.2 %% 2 * 7 * 4 - -3 <= 100 & TRUE