Apresentação
Disciplina ministrada na Universidade do Estado de Santa Catarina (Udesc), no Departamento de Ciência da Computação (DCC) do Centro de Ciências Tecnológicas (CCT), campus Joinville/SC, para o curso de Bacharel em Ciência da Computação, durante os semestres de 2008 e 2009.
Objetivo
Ementa
Horário
Softwares Utilizados
Notas de Aula
Ranges e List Comprehension
Ranges
Ranges são uma forma de criar listas numeráveis de maneira aritmética. Portanto, qualquer limite numérico ou que apresente uma ordem crescente é um range. Tudo que pode ser enumerado, pode ser criado com um range. Números podem ser enumerados. Caracteres podem ser enumerados:
Assim, ao invés de escrever os números de 1 a 10 como:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
podemos escrever utilizando um range
[1 .. 20]
que o resultado será equivalente.
O mesmo é válido para caracteres
['a' .. 'z']
ou
['D' .. 'M']
Podemos também especificar um passo para o range. Por exemplo,
[1, 3 .. 30]
produzirá uma lista de 1 a 30, pulando os elementos de 2 a 2. Isso aplica-se também a caracteres
['a', 'd' .. 'z']
e é bastante útil para representarmos elementos enumeráveis em ordem decrescente. Por exemplo,
[30, 29 .. 0]
irá produzir uma lista com os valores de 30 à 0.
Com ranges podemos representar sequências de dados infinitas. Por exemplo, se quizermos todos os números maiores que 10, basta
[10, ..]
se quizermos todos os múltiplos de 13
[13, 26, ..]
claro que isso irá produzir uma sequência sem fim. Porém é bastante útil para situações onde queremos os primeiros 10 múltiplos de 13, por exemplo. Teríamos algo como
take 10 [13, 26 ..]
isso funciona graças à avaliação preguiçosa de Haskell, já que o interpretador não tem gerar toda a lista de números, preocupando-se primeiramente em coletar somente os 10 primeiros elementos da lista e só então gerando-a. As 15 primeiras letras do alfabeto poderiam ser obtidas portando por:
take 15 ['a', 'b' ..]
Algumas funções úteis quando trabalhamos com conjuntos infinitos são a cycle e a repeat:
- cycle
- toma uma lista e repete-a eternamente take 10 (cycle ['foo', 'bar'])
- repeat
- como o nome sugere, repete um elemento indefinidamente take 10 (repeat 2)
É interessante notar que quando trabalhamos com conjuntos infinitos de dados, devemos de alguma maneira limitá-los. Isso justifica o uso constante da função take.
List Comprehension
A compreensão de listas já é comum para nós. Usamos elas em matemática. Por exemplo, para representarmos os números naturais pares menores que 100, podemos fazê-lo através da fórmula
A = {x | x PERTENCE {1, 2, .. 100}, x mod 2 == 0}
onde x é chamada de função de saída, x PERTENCE {1, 2, .. 100} é o conjunto de entrada e x mod 2 == 0 é o predicado. Podemos representar exatamente esta lista de termos em Haskell através de
[x | x <- [1 .. 100], x `mod` 2 == 0]
O predicado é opcional. Se quizermos, por exemplo, os números de 2 a 20, podemos fazer
[x | x <- [2 .. 20]]
Se quizermos os números dobrados de 1 a 100, basta fazer
[x*2 | x <- [1 .. 100]]
ou os mesmos, só que somente os maiores de 20
[x*2 | x <- [1 .. 100], x*2 > 20]
Se quisermos os números de 50 a 1000 onde o resto da divisão deles por 6 seja 2, torna-se fácil usando compreensão
[x | x <- [50 .. 1000], x `mod` 6 == 2]
Ou os números ímpares de 1 a 10
[x | x <- [1 .. 10], odd x]
Ou se quizermos uma lista com strings iguais a "par" para os pares e "impar" para os ímpares:
[if (odd x) then "impar" else "par" | x <- [1 .. 10]]
Podemos também inclur quantos predicarmos quizermos:
[x | x <- [10 .. 20], x /= 13, x /= 15, x /= 19]
Ainda, podemos ter quantos conjuntos de entrada desejarmos. Por exemplo, para termos uma lista de tuplas de todas as combinações possíveis dos conjuntos [1, 2, 3, .. 10] e [100, 102, 104, .. 120] basta
[(x, y) | x <- [1 .. 10], y <- [100, 102 .. 120]]
Ou a multiplicação de todas as combinações possíveis de dois conjuntos
[x*y | x <- [2,5,10], y <- [8,10,11]]
Ou digamos que os senhor e senhora Silva querem escolher um nome para seu novo filho mas não sabem qual combinação seja a melhor. A compreensão de listas pode ajudar:
[ x ++ " " ++ y | x <- ["Luiz", "Pedro", "Paulo", "João", "Inácio"], y <- ["Silva", "da Silva"]]
Alguns outros exemplos interessante:
sum [1 | _ <- [10 .. 100] -- com isso podemos criar a função: tamanho x = sum [1 | _ <- x] [c | c <- "Oi João Pedro", c `elem` ['A'..'Z']] -- lembre-se, strings são listas!
Exercícios e Trabalhos Práticos
- Exercício sobre Ranges e List Comprehensions: analise os dois últimos exercícios dados em sala de aula e resolva-os (quando aplicável) utilizando os conceitos de ranges e compreensão de listas.
- Entrega: 19/05 - 23:59 no email do prof.
Referências
- SÁ, Claudio C. de and SILVA, Márcio F. da. Haskell: Uma Abordagem Prática. Novatec Editora, 2006.
- O'SULLIVAN, Bryan; STEWART, Don and GOERZEN, John. Real World Haskell. O'Reilly Press, 2008. Disponível online em http://book.realworldhaskell.org/read/.
- LIPOVACA, Miran. Learn You a Haskell for Great Good. Disponível online em http://learnyouahaskell.com/.
- TAKASHI. Prolog in Haskell. Disponível online em http://propella.blogspot.com/2009/04/prolog-in-haskell.html.