Instituto Federal de Mato Grosso do SulSetembro/2021

IFMS marca

05. Higher-order Functions

Algoritmos 2

Prof. Rodrigo Duran

Nesta aula vamos:


  • O que são e como utilizar Higher-order Functions (Funções de Alta Ordem)

    Vamos definir o que são Higher-order Functions e como utilizar as funções de alta ordem mais utilizadas em JavaScript.

Higher-order Functions (HoF)

Na idade média cavaleiros pertenciam a ordens. Certamente nenhum de estirpe tão superior quanto o Black Knight. Na dúvida, chamamos de empate.

Funções

Funções (até agora)


Os programas desenvolvidos até agora na disciplina fazem uso extensivo de funções, sejam elas já prontas (bibliotecas ou padrão da linguagem) ou funções definidas por nós mesmos.


Para chamar uma função basta entender corretamente os seus parâmetros...

Praticamente qualquer coisa pode ser impressa no console
console.log("Hello World!")

Repartindo um array ...
meuarray.slice(2)

Removendo um elemento do array
meuarray.pop()

Funções (até agora)


Ou podemos definir nossas próprias funções e posteriormente chamá-las

const dobro = function(valor){
  return 2*valor
}

console.log(dobro(2))
4

console.log(dobro(4))
8

Visualizando Funções


Funções (uma nova sintaxe)


A partir do padrão ECMAScript 6 é possível definir funcões com uma sintaxe mais compacta, chamada de Arrow Function.Arrow functions não substituem completamente a forma tradicional de definição de funções. Veja as restrições.

As duas funções tem o mesmo objetivo, a mesma semântica mas sintaxes diferentes.
let dobro_f = function (valor){
  return 2*valor
}
O parâmetro vem antes do arrow (=>). O corpo da função vem após o arrow (return já está implicito).
let dobro_a = valor => 2*valor

console.log(dobro_f(2))
4

console.log(dobro_a(2))
4

As arrow functions também podem ter mais de um parâmetro...

Os parâmetros ficam entre parêntesis.
const soma_f = function (valor, adicionar){
  return valor + adicionar
}
const soma_a = (valor, adicionar) => valor + adicionar
console.log(soma_f(1,3))
4

console.log(soma_a(1,3))
4

Caso o corpo da função tenha mais de um comando, inserir bloco (entre chaves)
const soNegativaIndicePar = function (valor, indice){
  if (indice % 2 == 0){
    return (valor * -1)
  }
  else{
    return valor
  }
}
const snip = (valor, indice) => {
  if (indice % 2 == 0){
    return valor*-1
  }
  else{
    return valor
  }
}

QUIZ!

Qual é a equivalente à função quadrado usando arrow functions?

Acesse o link do exercício

let quadrado = function (n){
  return n*n;
}

let quad = ______________________

QUIZ!

Escreva uma função, utilizando arrow functions que retorne o valor inicial (ini) multiplicado por um acréscimo (ac).

Acesse o link do exercício

multi(10,2)
//20
multi(2,2)
//4

QUIZ!

Escreva uma função, utilizando arrow functions, que adiciona +1 se o valor passado como parâmetro for positivo e adiciona +2 se o número for negativo

Acesse o link do exercício

adiciona(-2)
//0
adiciona(1)
//2

Higher-order Functions


Higher-order Functions (HoF) são funções que operam em outras funções: HoF tomam outras funções como parâmetros ou as retornam. Em JavaScript, funções são cidadãos de primeira classe (First-class citzens)First-class functions são funcões que são tratadas como uma outra variável qualquer: podem ser passadas como um argumento para outras funções, pode ser retornada por outra função e pode ser atribuída como um valor a uma variável.

const dobro = function(valor){
  return 2*valor
}
const quadrado = function(valor){
  return valor*valor
}
const negativo = function(valor){
  return -1 * valor
}
const matFunc = function(valor, func){
  return func(valor)
}

console.log(matFunc(3, dobro))
6
console.log(matFunc(3, quadrado))
9
console.log(matFunc(3, negativo))
-3

Visualizando HoF


As funções Super Poderosas!

Map      Filter      Reduce

Repetição Implícita


Arrays em JavaScript possuem métodos pré-definidos que atuam em cada elemento do array.

Cada método age de uma forma diferente, mas map, filter e reduce são formas de repetição implícita.

Esses métodos tomam como parâmetro uma função que vai ditar "o que será feito" com cada elemento do array.

MAP



Map


Map transforma cada elemento do array de acordo com a função usada como parâmetro. O resultado do map é um novo array com os valores transformados de cada elemento.

Função que retorna o dobro de um número. O parâmetro n corresponde a cada elemento do array
const dobro = function(n){
  return 2*n
}
Função que retorna o quadrado de um número
const quadrado = function(n){
  return n*n
}
let numeros = [1,2,3,4,5]
A função map aplica a função dobro a cada um dos elementos do array e retorna um novo array como resultado
let num_dobro = numeros.map(dobro)
console.log(num_dobro)

[2,4,6,8,10]
let num_quad = numeros.map(quadrado)
console.log(num_quad)

[1,4,9,16,25]

Visualizando Map


Map


Map também aceita um segundo parâmetro (além de cada elemento do array) o qual corresponde ao índice do elemento no array.

Função que retorna o dobro de um número, exceto o primeiro elemento
const dobro = function(val, index){
  if (index != 0){
    return val*2
  }
  else{
    return val
  }
}

let valores = [1,3,5,7,9];
Lembre-se que o parâmetro é a função a ser aplicada. O próprio map se encarrega de informar à função dobro sobre os valores (val) e o índice (index).
let nv = valores.map(dobro)
console.log(nv)
[1,6,10,14,18]

Visualizando Map


QUIZ!

Qual é a saída (impressa no console) após a execução do programa abaixo?

let inversa = function(valor){
  return -1 * valor
}

let coordenadas = [-0.3, -1.7, 4.2, 5.1, -2.0, 13.3, 5]
console.log(coordenadas.map(inversa))

QUIZ!

Canais RGB


O programa no exercício está incompleto. Cada pixel em uma imagem é dado por 3 valores (Vermelho, Verde, Azul) e Transparência (alpha). O objetivo do código do exercício é retornar o inverso de cada pixel em uma imagem. Para inverter cada canal da imagem, basta subtrair 255 do canal de cor. Isso deve ser feito para todos os canais, exceto o alpha.

Dica : para verificar se um número qualquer é múltiplo de 4, base que o resto da sua divisão inteira por 4 seja zero. A função módulo (%) em JavaScript calcula o resto da divisão inteira de um número por outro

Acesse o link do exercício

FILTER



Filter


Filter testa se cada elemento do array satisfaz a função parâmetro. Para cada elemento cujo o resultado da função é TRUE, esse elemento é inserido no novo array de resposta. Portanto, filter retorna um novo array contendo apenas os elementos que satisfazem a função.

Função que retorna se um determinado número é positivo
const negativo = function(n){
  return n > 0
}
As funcões usadas pelo filter devem retornar TRUE or FALSE

let numeros = [-1,0,-2,2,-3,5, -4]
Para cada elemento do array a função negativo é aplicada. Se o resultado da função for TRUE, o elemento é inserido em um outro array temporário. Ao fim da aplicação da função em todos os elementos o array temporário resultante é retornado como resultado.
let positivos = numeros.filter(negativo)
console.log(positivos)
[2,5]

Visualizando Filter


Filter


Filtrando todas as palavras que contém uma determinada frase (String)

let fruits = ['maca', 'banana', 'uva', 'manga', 'laranja']
Neste caso estamos usando uma função para filtrar todos os items de um determinado array que contenham uma determinada frase
function filtrarItems(arr, frase) {
Aqui usamos uma arrow function. el representa cada elemento do array. Checamos se a string el contem outra (sub)string frase. Caso contenha, indexOf retorna um valor >= 0.
  return arr.filter( el => el.indexOf(frase) >= 0)
}

console.log(filtrarItems(fruits, 'ma'))
["maca", "manga"]

console.log(filtrarItems(fruits, 'an'))
["banana", "manga", "laranja"]

Visualizando Filter


QUIZ!

Qual é a saída (impressa no console) após a execução do programa abaixo?

Acesse o link do exercício (Google Forms)

let bandas = ["The Beatles", "Rolling Stones", "Pink Floyd", "Led Zeppelin", "Queen", "The Beach Boys"]
let sm = bandas.filter( nome => nome.length > 12)
console.log(sm)

QUIZ!

Considere que exista um array com as idades de todos os alunos da disciplina de Algoritmos 2 (ou seja, todos os estudantes da turma INFO 2). Escreva um programa que filtre apenas os estudantes maiores de 14 anos.

Acesse o link do exercício

REDUCE



Reduce


Reduce, diferentemente de map e filter, não retorna (necessariamente) um array como resultado. Reduce retorna um único elemento resultante da aplicação da função nos elementos do array. Para tanto, reduce introduz um novo parâmetro que serve como acumulador para o resultado parcial.

Soma todos os elementos de um array
let somador = function (acumulador, valor) {
  return acumulador + valor
}
let numeros = [2 , 4 , 6, 8, 10]
O acumulador é necessário pois armazena os resultados parciais! Pense nele como um acumulador temporário.
O segundo parâmetro da função reduce é o valor com o qual o acumulador será inicializado!
console.log(numeros.reduce(somador, 0))
30
Reduce retorna um valor (ou elemento) e não um array nesse caso!

Visualizando Reduce


O Poder da Composição

Encadeando HoF e tornando seu código muito mais enxuto (e legível)!

Compondo HoF


Uma grande vantagem oferecida por HoF é a composição. Como map e filter retornam um array, podemos imediatamente encadear uma outra HoF a partir do resultado da anterior.

Dessa forma, nós podemos criar funcões menores que gerenciam um (ou poucos) objetivos. Podemos compor funcões mais complexas utilizando várias funcões menores.

Esta técnica pode reduzir o número de bugs e fazer com que o código seja mais fácil de ser compreendido. Você pode compor quantas funções achar necessário.

Função que retorna se um determinado número é positivo
const negativo = function(n){
  return n > 0
}


Soma do resultado parcial, mais o elemento atual
let soma = function (n, acc){
  return acc + n
}
let numeros = [11,-4, -3, -8, 10, -3, 4, -7, 9, -11]

Primeiro cada elemento do array é aplicado à função negativos. Caso o resultado da função para aqele elemento seja TRUE (ou seja, o número é maior do que 0), o elemento é adicionado a um array temporário. Ao final do método filter, esse array é retornado e imediatamente encadeado com o método reduce. Reduce aplica a função soma a todos os elementos do array (acumulando os resultados parciais) e retornando o valor da soma.
O encadeamento é feito através da chamada do método usando o ponto (.)
let s = numeros.filter(negativos).reduce(soma)

console.log(s)
30

Apenas items contendo a substring "la" e com tamanho superior a 5 caracteres


Função que retorna se uma determinada string contém a string "la"
let contem_frase = function (elem) {
  return elem.indexOf("la") >= 0
}
Função que retorna se uma determinada string tem tamanho maior do que 5
let maior_que = function (elem){
  return elem.length > 5
}


let produtos = ["caderno", "lapis", "borracha", "lapiseira", "papel"]
Primeiro filtro retorna um array com os elementos que contém a frase (lapis e lapiseira), que é composto (serve de entrada) para o segundo filtro que retorna apenas os elementos (do resultado anterior) com tamanho maior do que 5.
let procura = produtos.filter(contem_frase).filter(maior_que)
console.log(procura)
lapiseira

Visualizando a Composição