Controle de Fluxo 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ógico if() e else
if(condicao){
    Instruções1
} else {
    Instruções2
}
A condição para o teste deve ser uma comparação lógica resultando em TRUE ou FALSE. Instruções1 serão executadas se a condição for verdadeira, Instruções2 caso contrário.

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

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

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

> # ifelse
> m <- 4
> ifelse(m==3, "é", "não é")
[1] "não é"
> m <- 3
> ifelse(m==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, "aprovado", "reprovado")
> resultado
[1] "reprovado" "reprovado" "reprovado" "aprovado" "reprovado" "aprovado" "reprovado"

> # A operação acima tem idêntico resultado à:
> teste <- c(1,2.3,4,5.5, 2.3, 7.3, 0.9)
> resultado <- NULL
> for (i in 1:length(teste)) {
       if (teste[i]>5) resultado[i] <-"aprovado"
       else resultado[i]<- "reprovado"
       }
> resultado
[1] "reprovado" "reprovado" "reprovado" "aprovado" "reprovado" "aprovado" "reprovado"
> # A primeira forma, além de mais compacta, é mais eficiente e rápida.

> # Uso de switch() com argumento inteiro:
> print(switch(3,"um", "dois", "três", "quatro"))
[1] "três"
> # Outros exemplos de uso de switch() abaixo, com a instrução for
Laço for()
for(condicao) {
    Instruções1
}
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) {
    Instruções …
}
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.

Laço repeat()
repeat() {
    Instruções …
}
Executa as instruções indefinidamente. Uma saída para este laço pode ser forçada com a instrução break.

As seguintes instruções são usados juntamente com os laços for, while e repeat

Instrução Efeito
break força a saída de um laço
next pula uma iteração do laço (retornando para seu início)
return retorna o valor de uma função
> # 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, 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
> # Observe que a variável continua existindo após o loop
> print(i)
[1] 10
> # 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 Instruções
     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 quando a função é invocada.

> 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
A função args() pode ser usada em sessões interativas para mostrar os argumentos de uma função. Para descobrir quais são esses argumentos e seus valores default programaticamente use a função formals().

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).
A instrução de return, embora não obrigatória, pode ser útil para interromper o fluxo de comandos, forçando o término da função. No exemplo abaixo calculamos, apenas como exercício, o fatorial de um escalar. Claro que R já tem uma função fatorial embutida que calcula fatorial em vetores e matrizes.

> fat <- function(n) {
	 m <- as.integer(n)
	 if (length(n)!=1) return("O argumento deve ser um escalar")
	 if (m!=n) return("O argumento deve ser inteiro")
	 if (m<0) return("O argumento deve ser positivo")
	 return(ifelse(m==0, 1, prod(1:m))
 }
> fat(4.3)
[1] "O argumento deve ser inteiro"
> fat(-3)
[1] "O argumento deve ser positivo"
> fat(1:2)
[1] "O argumento deve ser um escalar"
> fat(0)
[1] 1
> fat(9)
[1] 362880
> # Usando factorial
> u <- 1:9
> dim(u)<-c(3,3)
> u
	 [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> factorial(u)
	 [,1] [,2]   [,3]
[1,]    1   24   5040
[2,]    2  120  40320
[3,]    6  720 362880

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"


Programação com R


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 *