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
1
ou"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
TRUE
eFALSE
.
- 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 anterior
Com 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:
-
y
pode ser um novo nome, associado ao mesmo objeto – mesma posição na memória do computador – quex
.
-
y
pode 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 quex
també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
x
nã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^4
vira625
. -
/
é analisado a seguir.- Como é da esquerda pra direita, primeiro
9/3
vira3
, e depois3/625
vira0.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 <- 3
e 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
1
e 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
b
apó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
TRUE
sex
for múltiplo de 2. - Uma frase que retorne
TRUE
sex
não for múltiplo nem de 3, nem de 5. - Uma frase que retorne
TRUE
se a parte inteira da divisão dex
por 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