Introdução à Programação Funcional
Introdução à Programação Funcional
Sumário
1. Motivações para lambda calculus
Sumário clicável
Voltar ao sumário
A programação funcional é um paradigma que usa Olá
funções e expressões matemáticas para resolver
problemas computacionais. Destaca-se pela
imutabilidade dos dados, priorizando funções como
elementos centrais. O Cálculo Lambda de Alonzo
Church é fundamental nesse paradigma. Conceitos-
chave são funções anônimas, expressões lambda,
recursividade e modularidade. A programação
funcional usa estruturas de dados imutáveis, como
listas, tuplas e maps. Proporciona flexibilidade na
composição de funções e reutilização de código. A
imutabilidade garante previsibilidade e facilidade de
teste. Resumindo, a programação funcional oferece
uma abordagem poderosa, baseada em funções,
expressões matemáticas, imutabilidade de dados e
técnicas como recursividade e modularidade.
4
1.
Voltar ao sumário
Motivações para lambda calculus
Em meados de 1930, existia uma forte discussão acadêmica acerca da definição formal
de Computabilidade, pois, intuitivamente, algumas coisas são altamente computáveis, ou
seja, são facilmente resolvíveis através da aplicação de um algoritmo de passos finitos,
como, por exemplo: o cálculo de uma raiz quadrada; o cálculo do inverso de uma String,
a soma de dois números, a verificação se um número é primo, ou se o número é positivo,
dentre outros, porém uma pergunta precisava ser respondida: Qual é o conjunto de todas
as coisas que são computáveis?
No mesmo ano Alonzo Church publicou seu artigo “An unsolvable problem of elementary
number theory”, apresentando um sistema formal para expressar computação baseado em
abstrações de funções e aplicação usando apenas atribuições de nomes e substituições.
Esse sistema ficou conhecido como cálculo λ. Ambos os artigos concluíram que o
problema enfrentado era de decisão e, como resultado, concluíram que esse problema
não era computável. Ainda em 1936, Turing, após conhecer o artigo de Church, anexa um
apêndice ao seu artigo, afirmando que o seu conceito de computabilidade era semelhante
ao conceito de efetivamente calculável.
Atualmente, a definição de computável é definida como qualquer problema que possa ser
resolvido pelo cálculo λ ou em uma máquina de estados finita de Turing. Cientes disso,
podemos analisar quais são os tipos de problemas associados a essa solução:
5
Voltar ao sumário
• Função: calcular o resultado da aplicação de uma função:
Exemplo: inverter uma String.
• Busca: verificar se xRy em uma relação binária R.
Exemplo: buscar um nó em um grafo.
• Otimização: encontrar a solução x* entre todas as soluções do espaço de busca S
de tal forma a maximizar ou minimizar uma função f(x).
Exemplo: quanto devo colocar em cada possível investimento para maximizar
meus lucros.
Note que todo esse debate acaba levantando outro questionamento interessante: Qual é
a menor linguagem universal possível? Nas linguagens que você estudou até agora, no
curso de Análise e Desenvolvimento de Sistemas, todas elas apresentam:
Análise e
Desenvolvimento
de Sistemas
Porém, existe uma linguagem que somente utiliza funções: o cálculo λ, tendo sua
linguagem descrita em função de sua sintaxe e semântica. Sua Sintaxe é composto por
3 elementos:
• Variáveis: são símbolos usados para representar valores que podem variar. Elas
são usadas para expressar relações matemáticas e permitir o cálculo de funções
em diferentes pontos. Por exemplo, em uma função f(x), a variável x representa um
valor que pode ser substituído por um número real para obter o valor correspondente
da função.
6
Voltar ao sumário
• Definição de funções: especifica a relação entre uma variável independente e uma
variável dependente. Em geral, uma função é representada por uma expressão
matemática que relaciona a variável independente (geralmente denotada por x)
com a variável dependente (geralmente denotada por f(x) ou y). Por exemplo, a
função f(x) = 3x é uma definição de função que associa a cada valor de x o triplo
desse valor.
• Aplicação de funções: envolve a substituição de um valor específico na variável
independente para determinar o valor correspondente na variável dependente. Isso
significa substituir o valor dado para a variável x na expressão da função e calcular
o valor resultante. Por exemplo, se tivermos a função f(x) = 3x e quisermos calcular
o valor de f(3), devemos substituir x por 3 na função: f(3) = 3 * 3 = 9. Portanto, a
aplicação da função f(x) = 3x para x = 3 resulta em f(3) = 9.
e :: = x
| \x -> e
| e1 e2
Um programa é definido por uma expressão e, ou termos -λ que podem assumir uma de
três formas:
\x -> e,
//x é o parâmetro formal, e é o corpo da função
para qualquer valor x compute e
7
Voltar ao sumário
Funções de dois ou mais argumentos
Quando dizemos que algo é um Syntactic Sugar, estamos nos referindo a uma construção
ou uma notação que permite escrever um determinado trecho de código de forma mais
curta ou com uma sintaxe mais familiar, sem alterar o comportamento ou a semântica
subjacente do código. Essa notação mais amigável e concisa facilita a compreensão do
código e reduz a quantidade de código necessário para alcançar o mesmo resultado, ou
seja, é uma maneira de tornar a linguagem de programação mais expressiva, evitando
que o programador precise escrever código mais prolixo e complexo para realizar tarefas
comuns. A seguir, apresentaremos alguns exemplos de Syntactic Sugar encontrados em
várias linguagens de programação:
Outro tema interessante que deve ser abordado por quem está iniciando seus estudos
em programação funcional é o escopo de uma variável. O escopo indica a visibilidade de
8
Voltar ao sumário
uma variável em uma determinada linguagem de programação, C ou Java, por exemplo.
int x=10; //x está visível aqui, mas y ainda não foi criado neste
ponto do código
if (x>5){
int y; //x e y estão visíveis neste ponto do código
}
//x está visível aqui, mas y não está visível neste ponto do código,
pois deixou de existir.
Na expressão
\x -> e
\x -> x
\x -> \y -> x
Por outro lado, x está livre (free) se não está dentro de uma abstração. Para finalizar
o aspecto sintático do cálculo lambda, abordaremos as expressões fechadas, também
conhecidas como expressões lambda, funções anônimas ou combinadoras, que são uma
construção fundamental. Elas permitem definir funções de forma concisa e expressiva,
sem a necessidade de criar uma função nomeada separada.
Uma expressão fechada é uma função sem nome que pode ser definida inline, no ponto em
que será usada, e geralmente é usada como um valor ou argumento de outra função. Elas
são chamadas de “fechadas” porque são autossuficientes e encapsulam seu ambiente,
incluindo as variáveis e constantes às quais têm acesso.
9
Voltar ao sumário
As expressões fechadas são especialmente úteis quando se trata de passar funções
como argumentos para outras funções ou quando se deseja criar funções de maneira
mais concisa. Elas permitem a criação de código mais modular, expressivo e legível,
evitando a necessidade de criar funções nomeadas separadas.
JavaScript
Haskell
main :: IO ()
main = do
let numbers = [1, 2, 3, 4, 5]
doubledNumbers = map (\x -> x * 2) numbers
print doubledNumbers
Elixir
numbers = [1, 2, 3, 4, 5]
doubled_numbers = [Link](numbers, fn x -> x * 2 end)
[Link](doubled_numbers)
Erlang
-module(main).
-export([start/0]).
start() ->
Numbers = [1, 2, 3, 4, 5],
DoubledNumbers = lists:map(fun(X) -> X * 2 end, Numbers),
io:format(“~p~n”, [DoubledNumbers]).
10
Voltar ao sumário
Python
numbers = [1, 2, 3, 4, 5]
doubled_numbers = list(map(lambda x: x * 2, numbers))
print(doubled_numbers)
Scala
F#
11
2.
Voltar ao sumário
Funções anônimas e expressões Lambda: introdução às
funções recursivas e recursividade em cauda
Isso a difere de outros paradigmas, cujo estado local ou estado global de um programa
podem influenciar no resultado da chamada de uma função.
Quadrado(n) = n * n
f: A -> B, y = f(x)
12
Dado o exemplo acima, já podemos perceber que existe uma diferença de caráter
Voltar ao sumário
semântico no uso de sinal de igual, se compararmos a programação imperativa, por
exemplo. Na programação imperativa, o igual significa atribuição, ou seja, atualização de
uma variável de memória (lado esquerdo da igualdade) com um novo valor (lado direito
da igualdade) e mudança do estado geral do programa (pois houve alteração de valores
de variáveis)
x = x + 1 (X recebe o valor de x + 1)
Como visto, o trabalho de Alonzo Church define a notação lambda, fornecendo um método
para definir funções não nomeadas. Uma expressão lambda especifica os parâmetros e
o mapeamento de uma função. Ela é a função propriamente dita, mas não nomeada. A
seguinte expressão define uma função que eleva um número ao quadrado:
(λx.x*x) //lambda de x é x * x
Antes de ser avaliada, a expressão vale para qualquer membro do conjunto domínio. No
entanto, após avaliada para um determinado parâmetro, ela é aplicada e resulta em um
valor. Exemplo e aplicação que resultam no valor 16:
Em Haskell, uma expressão lambda representando uma função sempre recebe um nome.
Associado a esse nome, é indicado o tipo da função, ou seja, os tipos respectivamente dos
elementos do domínio (Argumentos) e imagem (Retorno) da função. A função “exemplo”
abaixo recebe um inteiro e responde com um outro inteiro, e a expressão lambda é
declarada de maneira muito semelhante à sua forma teórica λx.(x2 + 2x + 5).
Dentro da linguagem Haskell, temos alguns detalhes que valem ser lembrados, um deles
nos lembra que nomes de funções e parâmetros sempre começam com letra minúscula,
enquanto que tipos começam com letra maiúscula. Não existem parênteses separando
o parâmetro do nome da função (conforme conversamos quando abordamos syntatic
sugar).
13
Voltar ao sumário
A declaração de tipo no Haskell é fortemente tipada, assegurando que a função seja
aplicada a argumentos dos tipos corretos ou esperados. Os tipos básicos da linguagem
incluem inteiros (Int e Integer), reais (Float, Double), booleanos (Bool) e caracteres (Char,
String). Nesta declaração, o último tipo mencionado é sempre o tipo do retorno, enquanto
que os demais tipos são os tipos dos parâmetros, em ordem, conforme podemos observar
na Figura 2 apresentada abaixo:
Figura 2 - Declaração de função
Função Definição
ceiling
abs Retorna o menor inteiro não menor que o argumento
cos
ceiling Retorna o cosseno de um número descrito em radianos
div
cos Retorna o quociente inteiro da divisão de dois inteiros
sqrt
div Retorna a raiz quadrada do argumento
truncate
sqrt Retorna o inteiro sem a parte fracionária do número
truncate
Fonte: elaborada pelo autor (2023)
== a == b A igual a b
/= a /= b A diferente de b
14
Voltar ao sumário
Quadro 3 – Operadores Aritméticos
+ a + b Soma
- a - b Subtração
* a * b Multiplicação
/ a / b Divisão
^ a ^ b Potência
Uma função também pode incluir estruturas condicionais, para desviar o fluxo do programa
para diferentes partes, dependendo de uma avaliação da condição (verdadeira ou falsa).
Em Haskell, podemos implementar essas estruturas de acordo com o exemplo abaixo:
15
Voltar ao sumário
É muito importante reforçar que o Haskell não utiliza chaves para delimitar partes do
código relacionadas. Redobre sua atenção com a indentação, pois linhas de código no
mesmo nível de indentação significam que elas pertencem a um mesmo bloco.
Por exemplo, vamos supor que queiramos calcular a área de um triângulo de lados a, b e
c, através da fórmula de Heron, dada por:
s = (a + b + c) / 2
Note que podemos construir definições locais escrevendo uma expressão let, formada
por uma lista de definições e um corpo (que é uma expressão), conforme vemos abaixo:
16
Voltar ao sumário
Quadro 4 – Definição e expressão
Expressão Resultado
Agora vamos juntar tudo, definindo uma função que calcula a área de um cilindro
Uma função recursiva é uma função definida em termos dela mesma. Possui três fases:
17
Voltar ao sumário
Na definição, o primeiro guarda estabelece que i fatorial de 0 é 1 (caso base). Já o segundo
guarda estabelece que o fatorial de um número positivo é o produto deste número e do
fatorial de seu antecessor. Esse é o caso recursivo e geral.
chamada
potenciade2emcauda 5 1
potenciade2emcauda 4 2
potenciade2emcauda 3 4
potenciade2emcauda 2 8
potenciade2emcauda 1 16
potenciade2emcauda 0 32
Por fim, apresentaremos a função fatorial ajustada para o requisito de função com
recursão em cauda.
Compreendidos os assuntos tratados até aqui? Então, vamos aprender mais, no nosso
próximo Circuito de Estudo!
18
3.
Voltar ao sumário
Módulos, funções de alta ordem, imutabilidade e efeitos
colaterais
Neste Circuito, vamos explorar cada um desses conceitos, apresentando sua importância
para a programação funcional.
1 import [Link]
2 numUniques :: (Eq a) => [a] -> Int
3 numUniques = length . nub
19
Voltar ao sumário
elementos únicos em uma lista específica. Ao utilizar a declaração «import [Link]»
em Haskell, passamos a acessar o módulo [Link] que fornece várias funções úteis
para manipulação de listas (linha 2 e 3). Abaixo estão algumas das principais funções
disponíveis nesse módulo:
Quadro 1 – Funções para manipulações de listas
Função Expressão
As funções de alta ordem são funções que recebem uma outra função como argumento;
retornam funções como resultado; recebem e retornam uma função. Essas funções são
particularmente úteis para criar funções genéricas que dependem de certas funções
externas, que serão passadas como parâmetro. Inicialmente focaremos nos processos
de mapeamento e filtragem.
20
Voltar ao sumário
A seguir apresentaremos um exemplo de definição de função em Kaskell:
Vamos considerar o exemplo acima, no qual temos uma função chamada soma que
recebe dois números e retorna a soma deles. Normalmente, para utilizar essa função,
somos obrigados a fornecer os dois argumentos:
Chamada Resultado
soma 3 5 8
Chamada Resultado
soma3 = soma 3
resultado = soma3 2
Na linha soma3 = soma 3, estamos criando uma nova função chamada soma3 que é uma
aplicação parcial da função soma. Ela fixa o primeiro argumento como 3 e espera apenas
o segundo argumento. Portanto, quando chamamos soma3 2, estamos fornecendo o
argumento restante e obtendo o resultado esperado, que é 5 (3 + 2).
A aplicação parcial é útil porque nos permite criar funções mais especializadas a partir de
funções mais gerais. Isso pode simplificar o código, tornando-o mais legível e reutilizável.
Além disso, a aplicação parcial está intimamente relacionada ao conceito de currificação,
que é uma propriedade fundamental em Haskell e outras linguagens funcionais, onde
as funções sempre recebem um único argumento e retornam uma nova função como
resultado, se necessário.
Como parte da utilização do cálculo lambda, as funções podem ser definidas da seguinte
forma:
21
Voltar ao sumário
Quadro 2 – Definições de funções
Chamada Resultado
2:[] [2]
6:8:[] [6,8]
4:[6,8] [4,6,8]
Recapitulando, funções de alta ordem são aquelas que operam sobre outras funções,
ou seja, que podem receber outra função com argumento ou retornar uma função como
resultado. Como exemplo, abordaremos duas funções muito utilizadas no processamento
de listas: mapeamento e filtros. Observe o código abaixo que implementa uma função que
recebe uma lista e retorna outra lista com os valores dos elementos multiplicados por 2.
Agora imagine que precisemos construir uma função que calcule o quadrado de um
número, a única troca para resolver a questão, seria substituir (x + x) por (x * x). É possível
implementar uma função de alta ordem que recebe uma função f do tipo (Int -> Int) como
argumento e a aplicar uniformemente em todos os elementos da lista, retornando uma
lista de inteiros mapeada.
O que nós buscamos, com a implementação de uma função de alta ordem, é a criação de
uma única função que trabalhe com as duas situações apresentadas, tanto com a função
dobra, quanto com a função quadrado.
22
Voltar ao sumário
dobra :: Int -> Int
dobra x = x + x
//Chamada
mapInt dobra [10, 20, 30, 40]
//resultado
[20, 40, 60, 80]
//Chamada
mapInt quadrado [1, 2, 3, 4]
//resultado
[1, 4, 9, 16]
Como próximo exemplo, vamos criar uma função de alta ordem chamada “Moema”, que
recebe como argumento uma função de teste e seleciona os elementos da lista que
satisfazem a condição desejada.
23
Voltar ao sumário
//chamada
lista = [1 .. 20]
moema pares lista
//resultado
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Chamada Resultado
Os módulos em Haskell também podem exportar uma lista específica de funções, tipos
ou typeclasses para serem visíveis para outros módulos. Isso é feito usando a palavra-
chave export, seguida pela lista de elementos a serem exportados.
Através do uso de módulos, é possível criar uma estrutura organizada para o código
Haskell, dividindo-o em partes menores e mais gerenciáveis. Isso facilita a manutenção,
reutilização e colaboração em projetos de programação funcional.
24
Voltar ao sumário
No exemplo apresentado, para o módulo [Link], que contém uma variedade de
funções úteis para manipulação de listas, essas funções são exemplos de mapeamento
e filtragem. O mapeamento consiste em aplicar uma função a todos os elementos de
uma lista, enquanto a filtragem envolve selecionar os elementos que satisfazem uma
determinada condição.
Além disso, no contexto das funções de alta ordem, foram apresentados exemplos de
aplicação parcial e currificação. A aplicação parcial consiste em fixar determinados
argumentos de uma função para obter uma nova função especializada, e a currificação é
o conceito de que as funções recebem um único argumento e retornam uma nova função,
se necessário.
25
4.
Voltar ao sumário
Organização e manipulação de dados imutáveis: listas,
tuplas e maps
Para declarar uma lista, é necessário informar o tipo da lista entre colchetes, como uma
lista de inteiros [Int} ou de caracteres [Char] ou de valores lógicos [Bool].
Nessas listas, os elementos são definidos através da inserção dos valores entre os
colchetes, separando cada valor do outro através de uma virgula, como podemos observar
abaixo:
listanum = [1, 2, 3, 4, 5]
listavazia = []
listachar = [‘a’,’b’,’c’,’d’,’e’]
Vale ressaltar que além da inicialização simples, as listas também podem ser definidas
através da criação de lista de listas:
1 ls1 = [1 .. 10]
2 ls2 = [1,3 .. 10]
3 ls3 = [10, 8 .. 0]
4 ls4 = [1,1,2,3,5,8..50]
Na linha 01 foi criada uma lista contendo os elementos numerados em sequência: [1,
2, 3, 4, 5, 6, 7, 8, 9, 10]. Já na linha 2 foi criada uma lista com os elementos ímpares
começando em 1 indo até 10: [1, 3, 5, 7, 9]. Na linha 03 foram criados os números pares
em ordem decrescente indo de 10 a 0: [10, 8, 6, 4, 2, 0]. Por fim, na linha 04, temos uma
lista criada para representar os primeiros elementos da série de Fibonacci, composta
pelos elementos: [1, 1, 2, 3, 5, 8, 13, 21, 34].
26
Voltar ao sumário
O Haskell oferece dois operadores básicos para manipulação de listas. O operador
interfixado “:”, que é um elemento e uma lista (que pode estar vazia) como seus
argumentos, possibilita a construção de uma lista, elemento por elemento. Importante
lembrar que o parâmetro da esquerda sempre será um elemento e o parâmetro da
esquerda sempre será uma lista, conforme podemos observar abaixo:
Quadro 1 – Elemento e parâmetro
Chamada Resultado
2:[] [2]
6:8:[] [6,8]
4:[6,8] [4,6,8]
A função head recebe uma lista como argumento e retorna o primeiro elemento dessa
lista. Por exemplo, se tivermos uma lista ls1 = [1, 2, 3, 4], a chamada da função head
ls1 retornará o valor 1. No entanto, é importante notar que a função head só pode ser
chamada em listas não vazias. Se tentarmos chamar head em uma lista vazia, ocorrerá
um erro.
A função tail, por outro lado, recebe uma lista como argumento e retorna uma nova lista
contendo todos os elementos, exceto o primeiro. Continuando com o exemplo anterior,
se chamarmos tail ls1, obteremos a lista [2, 3, 4]. Novamente, assim como head, a função
tail só pode ser chamada em listas não vazias, pois não é possível remover o primeiro
elemento de uma lista vazia.
Baseados nos comandos “:” tail e head, podemos entender que a lista [1, 2, 3, 4] pode
ser escrita como 1 : [2, 3, 4], onde 1 :: Int é a cabeça da lista (head) e a lista [2, 3, 4] :: Int
é a sua cauda (tail). Logo uma lista ou é vazia ou é uma lista com cabeça seguida de
cauda. Abaixo implementamos uma função recursiva que calcula o comprimento da lista
de inteiros:
Outro exemplo é o programa abaixo que recebe uma lista e devolve outra lista, que contém
os valores da primeira lista elevados à quinta potência
27
Voltar ao sumário
penta:: Int -> Int
penta x = x * x * x * x * x
Nota-se que a função aQuinta não possui recursão em cauda. Na chamada recursiva,
realizamos o processamento da cabeça (elevando o número à quinta potência) para
posteriormente conectá-lo à cauda da lista que ainda vai ser processada recursivamente.
Assim, pela aplicação da função acima, obteremos o seguinte resultado para a entrada
testada:
lista = [1 .. 9]
aQuinta lista
//Resultado
[1, 32, 243, 1024, 3125, 7776, 16807, 32768, 59049]
O Haskell também oferece um operador útil para realizar a concatenação de duas listas.
pares = [0, 2 .. 8]
impares = [1, 3 .. 9]
pares ++ impares //gerará a lista resultante [0, 2, 4, 6, 8, 1,
3, 5, 7, 9]
[1, 2, 3] ++ [4, 5, 6] //gerará a lista resultante [1, 2, 3, 4,
5, 6]
Uma lista também pode ser definida através de um critério Gerador, como exemplo: crie
uma lista de todos os pares (2*x) tal que x é uma lista de números entre 0 e 10.
Uma extensão dessa lista poderia ser definida para gerar uma lista infinita de números
pares:
28
Voltar ao sumário
Isso é possível devido à implementação do mecanismo de avaliação lenta, que se refere
à estratégia de avaliação adotada pela linguagem Haskell, em que as expressões não
são avaliadas imediatamente, mas apenas quando seu valor é necessário. Isso significa
que a linguagem Haskell usa a avaliação lenta como a estratégia de avaliação padrão. A
avaliação lenta é possível devido ao mecanismo de lazy instantiation, que é uma técnica
usada por Haskell para implementar a avaliação lenta.
A
Escolhe um elemento do vetor
para ser o pivô.
B
Particiona o vetor de maneira que
todos os elementos anteriores ao pivô
sejam menores do que ele, e todos os
elementos posteriores sejam maiores.
C
Ordena recursivamente os vetores
de elementos menores e maiores.
29
Voltar ao sumário
Tuplas
Uma Tupla é uma coleção de valores que pode ou não ter tipos diferentes, os valores são
colocados entre parênteses e separados por vírgulas, como no exemplo: (“Unifor”,1)
Os valores de tupla são definidos de maneira semelhante às listas, mas com a utilização
de parênteses () em vez de colchetes [].
Além disso, uma tupla pode conter calores de tipos diferentes, enquanto uma lista não.
Note que dentro da tupla a ordem dos elementos importa, de maneira que a tupla (1,
“Unifor”) é diferente da tupla (“Unifor”,1).
Quando a tupla possui dois membros, ela é chamada de “par” ou 2-pla. Para esse caso
especial de tupla, foram desenvolvidas duas funções, uma para acessar o primeiro
elemento da tupla (fst) e uma para acessar o segundo elemento da tupla (snd):
O uso do comando type permite que o desenvolvedor crie um sinônimo para um tipo. Sua
utilização não cria um novo tipo, apenas dá um apelido a tipos já existentes, tornando a
legibilidade do programa melhor.
Com base na definição do tipo Time, podemos elaborar funções para acessar os elementos
da tupla, utilizando casamento de padrões. Para exemplificar, vamos criar uma função
para apresentar os times classificados para a fase final da competição.
30
Voltar ao sumário
brasileirão = [(“Botafogo”,18), (“Palmeiras”,15),
(“Fluminense”,13), (“Atletico MG”,13),( “Cruzeiro”,11)]
classificados brasileirão 15
//Resultado
[“Botafogo”, ”Palmeiras”]
Para melhor fixar o uso de tuplas, vamos agora implementar um programa que crie a
representação de um ponto tridimensional (x,y,z), em que a sua representação será por
meio de uma tupla com a definição de um novo tipo e depois escreveremos uma função
que calcule a distância entre dois pontos passados como argumentos.
//Teste
p1 = (0, 0, 1)
p2 = (0, 0, 0)
distancia p1 p2
//Resultado
1.0
31
Voltar ao sumário
padroes :: Int -> String
padroes 1 = “Um!”
padroes 2 = “Dois!”
padroes 3 = “Tres!”
padroes x = “O numero“ ++ show x ++ “não está entre 1 e 3!”
Note que o exemplo apresentado acima, nomeamos os demais inteiros com x para utilizá-
lo na mensagem dos casos que não foram tratados. Caso não precisemos manipular o
valor de argumento, poderíamos substituir o x por um _ indicando ao Haskell que não
iremos utilizar essa informação para nada dentro do casamento de padrões.
O chamado padrão curinga, representado pelo caractere “_” pode ser utilizado no Haskell,
tendo em mente o casamento de padrões próprio da linguagem, para representar dados
indefinidos (variáveis) ou dados que não sejam relevantes para o seu programa (não
precisam ser nomeados). A utilização do padrão curinga facilita a implementação de
funções que utilizam o casamento de padrões para chegar ao seu resultado, muitas
vezes, funcionando como uma alternativa para estrutura de controle if-then-else muito
grandes, conforme podemos observar abaixo:
f1 :: Int -> Int -> Int -> Int f2 :: Int -> Int -> Int -> Int
f1 xyz | (x == 1) = 10 f2 1_ _= 10
| (y == 2) = 20 f2 _ 2 _ = 20
| (y == 3) = 30 f2 _ _ 3 = 30
| otherwhise = 0 f2 _ _ _ = 0
Para aprofundar esse tema, que tal ver agora o material complementar?
32
Voltar ao sumário
REFERÊNCIAS
ASCENCIO, Ana Fernanda Gomes; CAMPOS, Edilene Aparecida Veneruchi de. Fundamentos
da programação de computadores: algoritmos, pascal, c/c++ e java. 3. ed. São Paulo:
Pearson Education do Brasil, 2012. (Cód.:83968)
33
UNIVERSIDADE DE FORTALEZA (UNIFOR) AUTOR
LEANDRO DA SILVA TADDEO
Presidência
Lenise Queiroz Rocha
Bacharel em Informática, Mestre em Informática Aplicada,
Vice-Presidência
26 anos de experiência em docência, 30 anos de experiência
Manoela Queiroz Bacelar
em TI. Pesquisador, Professor de graduação em Sistemas
Reitoria de Informação e professor de Pós-graduação em Design
Randal Martins Pompeu Digital. Atualmente, atua como analista judiciário, lotado
Vice-Reitoria de Ensino de Graduação e Pós-Graduação no setor de informática do Fórum Clovis Beviláqua.
Maria Clara Cavalcante Bugarim
Vice-Reitoria de Pesquisa
José Milton de Sousa Filho
Vice-Reitoria de Extensão
Thiago Braga Martins
Vice-Reitoria de Administração
José Maria Gondim Felismino Júnior
Diretoria de Planejamento
Marcelo Nogueira Magalhães
Diretoria de Tecnologia
José Eurico de Vasconcelos Filho
RESPONSABILIDADE TÉCNICA