42

Funções - Slidesslides.jarley.com/pf/pf-04-scala-funcoes.pdf · Funções Funções “puras” Equivalente às funções definidas na matemática. São funções sem estado (stateless)

  • Upload
    phamque

  • View
    284

  • Download
    0

Embed Size (px)

Citation preview

Funções

Relembrando um dos objetivos do paradigma de programação funcional: Escrever funções “puras” Na prática, uma função pura apresenta as seguintes

características: Possui um ou mais parâmetros; Realiza cálculos utilizando apenas os parâmetros recebidos; Retorna um valor; Sempre retorna o mesmo valor para os mesmos parâmetros

de entrada; Não utiliza ou modifica dados fora da função (efeito

colateral); Não é afetada por alterações de dados que ocorrem fora da

função (idem).

Funções

Funções “puras”

Equivalente às funções definidas na matemática.

São funções sem estado (stateless).

Facilitam a execução de programas concorrentes.

Entretanto,...

É difícil programar funções que não alteram estado.

Precisamos ler arquivos, consultar bancos de dados, imprimir o resultado na console, etc.

Funções

Em Scala, funções possuem equivalência com a definição de um método.

A definição pode ocorrer em qualquer parte do código.

Sintaxe geral:

def <nome>(<parâmetro>:<tipo>[,...]):<tipo>=<expressão>

Funções

Exemplos de definições de funções

O corpo da função é composto por uma ou mais expressões.

A última linha executada denota o retorno da função.

def multiplica(x: Int, y: Int): Int = { x * y }

var m = multiplica(5,10) //retorna 50

Funções

Exemplos de definições de funções

É opcional a declaração do tipo de retorno da função (inferência de tipos)

def max(x: Int, y: Int): Int = {

if (x > y) x

else y

}

OU

// se a função possui apenas um comando

def max2(x: Int, y: Int) = if (x > y) x else y

Funções

Observação importante

Na definição de funções recursivas é obrigatória a definição do tipo de função.

Exemplo:

def potencia(x: Int, n: Int): Long = {

if (n >= 1) x * potencia(x, n-1)

else 1

}

Funções

Funções podem ser aninhadas

Exemplo

def E(x: Float) = {

def F(y: Float) = x + y

F(2*x)

}

...

println(E(5))

> 15.0

Funções

Alguns cuidados com funções aninhadas O parâmetro y não é visível no corpo da função E.

Visível apenas dentro de F!

def E(x: Float) = {

def F(y: Float) = x + y

F(2*x)

}

Funções

Alguns cuidados com funções aninhadas

O que ocorre quando temos conflito de nomes em diferentes escopos?

var a: Int = 1;

def P = {

var b = 1;

def Q = {

var b = " b "; var c = 4;

println(a+b+c)

}

Q

println(a+b)

}

P

Funções

Alguns cuidados com funções aninhadas

Declaração de funções aninhadas com o mesmo nome

def max(a: Int, b: Int, c: Int) = {

def max(x: Int, y: Int) = {

if (x > y) x

else y

}

max(a, max(b, c))

}

...

var m = max(42, 181, 19) // retorna 181

Funções

Procedures

Funções que não possuem um valor de retorno.

A última linha executada é um comando e não uma expressão.

Retorna o tipo Unit.

Exemplos:

def imprimeSoma(x:Int, y:Int) = println(“Soma=“+(x+y))

...

// Mais de uma linha , obrigatório declarar Unit

def imprimeSoma2(x:Int, y:Int):Unit = {

println(“Primeira linha”)

println(“Soma=“+(x+y))

}

Exercício em Laboratório

P1 – 2013.2

5ª Questão (2,0 pontos): Dada a seguinte função A(m,n):

válida para valores inteiros não negativos de m e n, implemente uma versão recursiva da função em Scala e mostre o resultado da execução de A(1, 2).

Exercício 11

Reescrever os exercícios 4 e 5, usando funções.

Criar obrigatoriamente as versões recursivasdos problemas propostos.

Exercício 12

Escreva uma função recursiva em Scala que imprime os valores de 5 a 50, sem utilizar loops.

Exercício 13

Escreva uma procedure em Scala para imprimir o resultado do Exercício 8.

Funções

Mecanismos de definição de funções

Em linguagens funcionais, existem normalmente dois mecanismos para definição de funções.

O mecanismo tradicional (visto anteriormente)

Funções anônimas (derivadas do cálculo Lambda),

Funções

Mecanismos de definição de funções

Definição formal:

Seja λx.E uma expressão onde x é um identificador e Euma expressão que pode conter x (por exemplo, E -> x+1).

λ é uma abstração que define uma função anônima, ou seja, a função pode ser aplicada à expressão F, denotada como (λx.E)F

Como ocorre? Cada ocorrência de x em E é substituída por F e as operações restantes são realizadas.

Funções

Exemplo de função anônima com Lambda

Calcular o lambda (λx.3 · x +1)4

λx é mapeado como F(4)

O E da função é a expressão 3.4+1.

O valor final do lambda é 13

Funções

Definindo funções anônimas em Scala Outro nome para funções anônimas: funções literais

Exemplo: Função para incrementar o valor de uma variável var inc = (x:Int) => x+1

Note que não usamos a palavra reservada def, assumindo que inc seja uma variável.

x é equivalente ao identificador na expressão lambda. O operador ”=>” faz o papel do ponto na expressão. A expressão após ”=>” será avaliada sempre que um valor de função for aplicado.

Funções

Utilizando funções literaisvar inc = (x:Int) => x+1

...

var x = inc(7)

println(x)

> 8

Funções

Funções literais com mais de um argumento

Exemplo: Função que multiplica dois argumentos

var mul = (x: Int) => (y:Int) => x*y

println(mul(3)(4))

> 12

mul é chamada também de função de alta ordem.

Funções

Construção de funções de alta ordem em Scala Considere a função mul do slide anterior.

mul pode receber apenas um argumento n e retorna uma função, a qual multiplica o valor por n.

Exemplo:

var mul = (x: Int) => (y:Int) => x*y

var mulNovo = mul(3)

println(mulNovo(4))

> 12

Exercício 14

Escreva uma função anônima que calcula o máximo de três números inteiros.

Atenção: evite código imperativo para o problema.

Pense em uma solução funcional!

Funções Anônimas

Relembrando as características de funções

Funções podem ser aninhadas dentro de outras funções;

Funções podem ser atribuídas a uma variável;

Funções podem ser passadas como parâmetros de outras funções.

Passando funções como parâmetros

var quadrado = (x:Int) => x*x

var somaInteiroQuadrado = (x:Int) => (quadrado: Int => Int) => x + quadrado(x)

println(somaInteiroQuadrado(10)(quadrado))

...

>110

A função somaInteiroQuadrado possui dois parâmetros: um inteiro e uma função

Passando funções como parâmetros

var quadrado = (x:Int) => x*x

var somaInteiroQuadrado = (x:Int) => (quadrado: Int => Int) => x + quadrado(x)

var f = somaInteiroQuadrado(10)

println(f(quadrado))

...

>110

O retorno da função parcial é outra função!

Funções como parâmetros "just-in-time"

var f1 = (x:Int) => x*x

var f2 = (x:Int) => x+1

var funcaoAnonima = (x:Int) => (y: Int => Int) => x + y(x)

println(funcaoAnonima(10)(f1))

println(funcaoAnonima(10)(f2))

...>110>21

y é um parâmetro que representa uma função anônimaqualquer. funcaoAnonima possui um comportamento quepode mudar de acordo com os seus parâmetros!

Exercício em Laboratório

P1 – 2015.1

3ª Questão (2,0 pontos): A função somaInt, listada abaixo, calcula a soma dos inteiros entre a e b. A função

quadrado, também listada abaixo, retorna o quadrado de um número inteiro a. Crie uma função anônima em

Scala que calcula a soma dos quadrados dos inteiros entre a e b. Você deverá, obrigatoriamente, utilizar as funções

somaInt e quadrado na sua resposta.

def somaInt(a: Int, b: Int): Int = if (a > b) 0 else a + somaInt(a + 1, b) def quadrado(a: Int) = a * a

Currificação de Funções

Em Scala, podemos usar o conceito de currificaçãode funções

Transforma uma função que recebe mais de um parâmetro em uma função que recebe uma lista de parâmetros.

Currificação de Funções

Exemplo: f(a: Int, b: Int, c: Int) { … }

Invocando a função: f(a,b,c)

Currificando f:

f(a: Int)(b: Int)(c: Int) { … }

Invocando a função: f(a)(b)(c)

Vantagens Legibilidade do código

Possibilidade de usar funções parciais (passar apenasparte dos parâmetros)

Currificação de Funções Exemplo:// soma simples

def soma(x: Int, y: Int) = x + y

// com currificação

def somaCurrificada(x: Int)(y: Int) = x + y

println(soma(1, 2))

println(somaCurrificada(1)(2))

...

>3

>3

Currificação de Funções Exemplo:// Usando funções parciais

def somaCurrificada(x: Int)(y: Int) = x + y

val incrementa = somaCurrificada (1)_

var x = incrementa(2)

...

>3

Função parcial

Aplicação Parcial de Funções Outro Exemplo:

// n é divisível por m?

def nDivideM(m: Int)(n: Int) = (n%m==0)

val ehPar = nDivideM(2)_

println(ehPar(4))

println(ehPar(5))

...

>true

>false

Aplicação Parcial de Funções Mais um exemplo:

def ehMultiplo(x: Int, y: Int) = y % x == 0

val f = ehMultiplo _

val x = f(7, 20)

> x: Boolean = false

// Aplicando funções parciais com alta ordem

val ehMultiploDe3 = ehMultiplo (3, _: Int)

val y = ehMultiploDe3(78)

> y: Boolean = true

Recursão de calda

Um tipo especial de recursão é chamado de tail recursion (recursão de calda).

Ocorre quando uma função chama ela mesma na última operação do corpo da função, sem expressões adicionais.

Os compiladores conseguem otimizar as chamadas de funções com recursão em calda

Uso da anotação @tailrec

Recursão de calda

Exemplo prático

Fatorial recursivo (sem tail recursion)def factorial(i: BigInt): BigInt = {

if (i == 1) i

else i * factorial(i - 1)

}

A última operação é uma multiplicação. Primeiro, o compiladorchama factorial e depois realiza a multiplicação. Logo, nãopodemos aplicar a recursão de calda.

Recursão de calda

Alterando o código para usar recursão de calda

import scala.annotation.tailrec

def factorial(i: BigInt): BigInt = {

@tailrec

def fact(i: BigInt, accumulator: BigInt): BigInt =

if (i == 1) accumulator

else fact(i - 1, i * accumulator)

fact(i, 1)

}

Agora, a última operação da função fact é somente achamada recursiva.

Recursão de calda

Outro exemplo

Cálculo recursivo da potência

def potencia(x: Int, n: Int): Long = {

if (n >= 1) x * potencia(x, n-1)

else 1

}

A última operação é uma multiplicação. Não podemos aplicar arecursão de calda.

Recursão de calda

Solução com recursão de caldaimport scala.annotation.tailrec

@tailrec

def potencia(x: Int, n: Int, t: Int = 1): Int = {

if (n < 1) t

else potencia(x, n-1, x*t)

}

Agora, a última operação da função potencia é somente achamada recursiva.

Exercício 15

Escreva uma função de alta ordem que recebe um inteiro (por exemplo, y:Int) e retorna uma função. A função retornada deverá receber um inteiro (porexemplo, x:Int) e retornar a multiplicação de x e y.

Exercício 16

Crie uma função recursiva em Scala que calcula o Fatorial Duplo de um número. A definição do fatorial duplo é dada por:

Obrigatoriamente, use recursão de calda em sua resposta.