Skip to content

Latest commit

 

History

History
492 lines (413 loc) · 13.4 KB

funcional.org

File metadata and controls

492 lines (413 loc) · 13.4 KB

Programação Funcional

1 Apresentação da programação funcional

FUNÇÕES

São elementos de primeira ordem

  • Atribuí-las a variáveis
  • Guardá-las em listas
  • Passá-las como argumentos
  • Criá-las dentro de outra função
  • Retorná-las como resultado de uma outra função

Blocos básicos

  • Funções anônimas
  • Closures (funções escritas por outras funções)
  • Listas de funções

2 Motivar a necessidade da abordagem funcional

Arquivo que usa -99 para representar valores ausentes.

library(tidyverse)
library(xtable);
set.seed(42)
df <- data.frame(
    replicate(6, sample(c(1:10, -99), 6, rep = TRUE)))
names(df) <- letters[1:6];
xtable(df, size="\\small")

Objetivo: substituir -99 por NA. Como fazer?

3 Soluções ingênuas

Onde estão os erros semânticos?

3.1 #1

df$a[df$a == -99] <- NA
df$b[df$b == -99] <- NA
df$c[df$c == -98] <- NA
df$d[df$d == -99] <- NA
df$e[df$e == -99] <- NA
df$f[df$g == -99] <- NA

3.2 #2

fix_missing <- function(x) {
  x[x == -99] <- NA
  x
}
df$a <- fix_missing(df$a)
df$b <- fix_missing(df$b)
df$c <- fix_missing(df$c)
df$d <- fix_missing(df$d)
df$e <- fix_missing(df$e)
df$f <- fix_missing(df$e)

4 Uma solução mais sofisticada

Utilizar duas funções

  • fix_missing sabe como substituir -99 por NA
  • lapply sabe como aplicar algo para cada coluna do DF
    • Recebe três argumentos: lista, função, argumentos para a função

A Função lapply é um exemplo de funcional

(uma função que recebe outra função)

fix_missing <- function(x) {
  x[x == -99] <- NA
  x
}
# Aplicando para todas as colunas
df[] <- lapply(df, fix_missing)
# Aplicando para somente as cinco primeiras colunas
df[1:5] <- lapply(df[1:5], fix_missing)
df;
   a b  c  d  e  f
1 NA 9 NA  6  1  9
2 NA 2  3  7  6  9
3  4 8  6 10  5  5
4 10 8 NA  2 10  8
5  8 6 NA NA  5  1
6  6 8  2 NA 10 10

Ideia chave: composição de funções.

5 E se…

… os valores ausentes são representados por vários valores.

Solução ingênua: copiar/colar → criador de problemas

Solução melhor:

Utilizar uma função que cria outras funções (closure).

missing_fixer <- function(na_value) {
  function(x) {
    x[x == na_value] <- NA
    x
  }
}
fix_missing_99 <- missing_fixer(-99)
fix_missing_999 <- missing_fixer(-999)

6 #1: Funções anônimas

Declarando

function(x) x*x

Todas as funções tem formals, body, e um environment.

formals( function(x) x*x )
body( function(x) x*x )
environment( function(x) x*x )
$x
x * x
<environment: R_GlobalEnv>

7 Chamando funções anônimas

É obrigatório o uso dos parênteses

  • Para chamar a função
  • Para passar argumentos
( function(x) x*x ) (2)
# faz a mesma coisa que o seguinte
f <- function(x) x*x
f(2);
[1] 4
[1] 4

8 #2: Closure

“An object is data with functions. A closure is \linebreak a function with data.” — John D. Cook

Funções anônimas

  • Definir pequenas funções que não merecem um nome
  • Criar Closures

Elas levam consigo o escopo/ambiente de onde foram definidas

power <- function(exponent) {
  function(x) {
    x ^ exponent
  }
}
square <- power(2)
cube <- power(3)

9 Investigando a implementação de closures

square
function(x) {
    x ^ exponent
  }
<environment: 0x55d41594eec8>
cube
function(x) {
    x ^ exponent
  }
<environment: 0x55d41594e178>

O que mudou?

10 Verificando o escopo/ambiente de closures

as.list(environment(square))
$exponent
[1] 2
as.list(environment(cube))
$exponent
[1] 3

11 Estado mutável

Modificar valores no ambiente do pai; permite criar estados mutáveis.

  • Uma closure leva consigo o escopo/ambiente de onde foi definida.
new_counter <- function() {
  i <- 0
  function() {
    i <<- i + 1
    i
  }
}
counter_one <- new_counter()
counter_two <- new_counter()
counter_one()
counter_one()
counter_two()
[1] 1
[1] 2
[1] 1

12 #3: Lista de funções

Funções podem ser mantidas em listas.

(chamá-las em um único comando com lapply)

Vejamos uma lista com três formas de calcular a média de um vetor.

compute_mean <- list(
  base = function(x) mean(x),
  sum = function(x) sum(x) / length(x),
  manual = function(x) {
    total <- 0
    n <- length(x)
    for (i in seq_along(x)) {
      total <- total + x[i] / n
    }
    total
  }
)

13 Chamando uma função em uma lista

Obter uma referência a ela, e em seguida realizar a chamada.

x <- runif(1e7)
system.time(compute_mean$base(x))
system.time(compute_mean[[2]](x))
system.time(compute_mean[["manual"]](x))

Chamar todas elas com a função lapply

# Primeira forma
lapply(compute_mean, function(f) f(x))
# Segunda forma
call_fun <- function(f, ...) f(...)
lapply(compute_mean, call_fun, x)

Sendo assim, podemos

lapply(compute_mean, function(f) system.time(f(x)))
# E passar parâmetros com valores "fixos"
lapply(compute_mean, function(f) f(x))#, na.rm = TRUE))

14 Referência

Advanced R by Hadley Wickham