Laços e Funções do Usuário

Laços: Controle de Fluxo

O interpretador de R processa as linhas de comandos de modo sequencial, uma linha após a outra. Muitas vezes é necessário bifurcar o código ou repetir um conjunto de linhas, dependendo de certas condições. Para isso temos os laços (loops) e testes lógicos.

Teste lógicos if() e else
if(condicao){
    Instrucoes1
} else {
    Instrucoes2
}

A condição para o teste deve ser uma comparação lógica resultando em TRUE ou FALSE. Instrucoes1 serão executadas se a condição for verdadeira, Instrucoes2 caso contrário.

Teste vetorizado ifelse()
ifelse(condicão, valor1, valor1)
Retorna valor1 se condicao = TRUE, valor2 caso contrário.

Laço for()
for(condicao) {
    Instrucoes1 ...
}

A condição para os laços for devem ser sempre do tipo var in seq, onde a variável var percorre uma sequência.

Laço while()
while(condicao) {
    Instrucoes ...
}

Executa as instruções enquanto a condição for verdadeira. Deve-se ter o cuidado de providenciar um mecanismo de saída para este laço.

> # Teste if/else
> if (17 %% 2 == 0) {
     print("17 é par")
} else {
     print("17 é ímpar")
}
[1] "17 é ímpar"

> # ifelse
> ifelse(4==3, "é", "não é")
[1] "não é"
> ifelse(3==3, "é", "não é")
[1] "é"
> # A função ifelse realiza internamente um
> # loop nos componentes de um vetor (ou outro objeto)
> teste <- c(1,2.3,4,5.5, 2.3, 7.3, 0.9)
> resultado <- ifelse(teste > 5, "passou", "falhou")
> resultado
[1] "falhou" "falhou" "falhou" "passou" "falhou" "passou" "falhou"

> # laço for
> for(i in c(1,3,5,7)) {print(paste(i,"^2 = ",i^2, sep =""))}
[1] "1^2 = 1"
[1] "3^2 = 9"
[1] "5^2 = 25"
[1] "7^2 = 49"
> # Laço while
> n <- 1
> while(n < 5) {
    print(paste(n, "< 5"))
    n<-n+1
}
[1] "1 < 5"
[1] "2 < 5"
[1] "3 < 5"
[1] "4 < 5"
Laço repeat()
repeat() {
    Instrucoes ...
}

Executa as instruções indefinidamente. Uma saída para este laço pode ser forçada com a instrução break, listada a seguir.

Instrução break: força a saída de um laço
Instrução next: pula uma iteração do laço (retornando para seu início)
Instrução return: retorna o valor de uma função

Instrução switch()
switch(expr, valor1, ..., valorn)
Se expr é um inteiro i, retorna valori
Se expr é um string str, os demais argumentos devem nomeados e switch retorna valor correspondente ao name = str.

> # Laço repeat, o mesmo que while(TRUE)
n <- 1
> repeat {
    print(paste(n, "< 4"))
    n <- n+1
    if(n == 4) break
}
[1] "1 < 4"
[1] "2 < 4"
[1] "3 < 4"
> # Saltando dentro de um laço
> for (i in 1:10) {
    if(i<4 | i>6) next
     print(i)
}
[1] 4
[1] 5
[1] 6

> # Uso de switch() com argumento inteiro:
> for (i in 1:4) print(switch(i,"um", "dois", "três", "quatro" ))
[1] "um"
[1] "dois"
[1] "três"
[1] "quatro"
> # Uso de switch() com argumento de string:
> sinto <- c("medo", "alegria")
> for (i in sinto) {
     print(
     switch(i, triste = "alegre-se", 
               medo = "calma", alegria = "aproveita")
     )
  }
[1] "calma"
[1] "aproveita"

Funções do Usuário

O usuário pode criar funções em R de acordo com suas necessidades. Elas geralmente servem para armazenar uma série de instruções que será utilizada repetidamente ou apenas para organizar um bloco de código mais complexo. Funções possuem a seguinte estrutura básica:

funcao <- function(arg1, ..., argn) {
     lista de Instrucoes
     return(objeto)
}

A instrução return é opcional. Se omitida a função retornará o resultado da última operação realizada. Os colchetes podem também ser omitidos se a função consiste em apenas uma linha de código.

A função é chamada fornecendo-se seus argumentos
funcao(varg1, ..., argn)
Quando ela retorna um valor que será usado em seguida atribuímos seu valor a uma variável:
var <- funcao(arg1, ..., argn)

Qualquer objeto, ou nenhum, pode ser retornado pela função. Quanto aos argumentos eles podem ou não ser nomeados. Argumentos não nomeados devem ser identificados pela sua posição na chamada da função. Se forem nomeados eles podem receber valores default na definição da função que serão usados caso sejam omitidos na invocação da função.

> funcao1 <- function(x, y) {
            z <- x+y
            return(x + y^z) }
> funcao1(2, 3)
[1] 245
> # O mesmo resultado seria obtido se omitíssemos a instrução return:
> funcao1 <- function(x, y) x + y^(x+y)

> # Com argumentos nomeados e com valores default:
> funcao2 <- function(inicio=1,fim=10) {
             v <- inicio:fim
             return(v) }
> funcao2()
 [1]  1  2  3  4  5  6  7  8  9 10
> funcao2(5)  # apenas o primeiro arg é fornecido
[1]  5  6  7  8  9 10
> funcao2(,5)  # segundo arg é reconhecido pela posição
[1] 1 2 3 4 5
> funcao2(fim=13)  # segundo arg é reconhecido pelo nome
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13
> # A instrução return não é obrigatória,
> # nem os colchetes para uma função de única linha
modulo <- function(z) sqrt(Re(z)^2 + Im(z)^2)
> modulo(4+5i)
[1]  6.40312
> # A função tratará, sempre que possível, qualquer tipo de argumento
> funcao3 <- function(x, y) { return(x + y) }
> funcao3(c(1,2,3), c(4,5,6))
[1] 5 7 9
> # Você pode visualizar a constituição de uma função
> funcao1
function(inicio=1,fim=10) {return(inicio:fim)}
> # Para exibir seus argumento use:
> args(funcao1)
function (inicio = 1, fim = 10)
NULL

Pode-se também especificar que um argumento é nulo se não for declarado explicitamente na chamada da função como, por exemplo, em: f <- function(a, b = 1, c = NULL) {}. Neste caso deve-se testar no corpo da função se o argumento foi fornecido, antes de usá-lo. É importante notar que uma variável inicializada dentro do corpo de definição da função tem seu escopo limitado à esta função (e não pode ser usada fora dela).

O argumento ... (3 pontos) tem um significado especial em R. Ele indica que um número indeterminado de argumento podem ser passados para a função e é particularmente útil quando existe outra função aninhada (com muitos argumentos) no corpo da primeira.

> montarLinha <- function(x, ...){
                 print(paste("Existem ", x, "cores:", ...))
                 }
> montarLinha(3, "vermelho", "verde", "azul")
[1] "Existem  3 cores: vermelho verde azul"

Função podem ser aninhadas, ou seja, é possível chamar uma função de dentro de outra funcão:

> funcao1 <- function(txt) { return(paste("modificação 1: ",txt))}
> funcao2 <- function(txt, t2=NULL) {
             retorna <- ""
             if (is.character(txt)) {
                 retorna <- paste("modificação2: ", funcao1(txt))
             } else {
                 retorna <- "Argumento deve ser um string..."
             }
             if (!is.null(t2)) retorna <- paste(retorna,"!")
             return(retorna)
  }
> print(funcao2(2))
[1] "Argumento deve ser um string..."
> print(funcao2("testando"))
[1] "modificação2:  modificação 1:  testando"
> print(funcao2("Inserindo o 2o argumento", 1))
[1] "modificação2:  modificação 1:  Inserindo o 2o argumento !"

Funções podem retornar qualquer um dos objetos de R, inclusive outras funções:

> potencia <- function(ordem) {
              f <- function(x) {x ^ ordem}
              return(f)
              }
> quarta <- potencia(4)    # define a função f(x) = x^4
> quarta(2)
[1] 16
> quadrado <- potencia(2)  # define a função f(x) = x^2
> quadrado(15)
[1] 225

Observação: Fizemos uso das funções is.character(var) e is.null(var) que testam, respectivamente, se a variável var é do tipo character ou null. Muitas outras funções de teste existem e são muito úteis, principalmente em scripts. Associadas a elas estão as funções de conversão que forçam a transformação de um tipo em outro, quando possível.

Algumas destas funções estão listadas abaixo:

Teste Conversão
is.numeric() as.numeric()
is.character() as.character()
is.vector() as.vector()
is.matrix() as.matrix()
is.data.frame() as.data.frame()
is.factor() as.factor()
is.logical() as.logical()
is(var, type) as(var, type)
> is.character("1")          # TRUE
> is("1", "character")       # TRUE
> is("1", "numeric")         # FALSE
> is(1, "numeric")           # TRUE
> is(1i, "complex")          # TRUE
> n <- as("125", "numeric")  # n = 125
> is.logical(1==2)           # TRUE
> is.vector(1:2)             # TRUE
> is.vector("1")             # TRUE (um vetor com um componente)
> as.logical(1)              # TRUE (as.logical(0) = FALSE
> dt<- as.Date("2018-11-25") # dt = "2018-11-25"
> class(dt)                  # "Date"


Aquisição de Dados

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *