\documentclass[a4paper]{article} \usepackage[a4paper,left=3cm,right=2cm,top=2.5cm,bottom=2.5cm]{geometry} \usepackage[sfdefault, book, lf]{FiraSans} % lf - lined numbers \usepackage[colorlinks=true]{hyperref} \usepackage{graphicx} \usepackage{cp2223t} \usepackage{subcaption} \usepackage{adjustbox} \usepackage[indent=12pt]{parskip} %================= lhs2tex=====================================================% %include polycode.fmt %format (div (x)(y)) = x "\div " y %format succ = "\succ " %format ==> = "\Longrightarrow " %format map = "\map " %format length = "\length " %format fst = "\p1" %format p1 = "\p1" %format snd = "\p2" %format p2 = "\p2" %format Left = "i_1" %format Right = "i_2" %format i1 = "i_1" %format i2 = "i_2" %format >< = "\times" %format >|< = "\bowtie " %format |-> = "\mapsto" %format . = "\comp " %format .=?=. = "\mathbin{\stackrel{\mathrm{?}}{=}}" %format -|- = "+" %format conc = "\mathsf{conc}" %format summation = "{\sum}" %format (either (a) (b)) = "\alt{" a "}{" b "}" %format (frac (a) (b)) = "\frac{" a "}{" b "}" %format (uncurry f) = "\uncurry{" f "}" %format (const (f)) = "\underline{" f "}" %format (lcbr (x)(y)) = "\begin{lcbr}" x "\\" y "\end{lcbr}" %format (lcbr3 (x)(y)(z)) = "\begin{lcbr}" x "\\" y "\\" z "\end{lcbr}" %format (split (x) (y)) = "\conj{" x "}{" y "}" %format (for (f) (i)) = "\for{" f "}\ {" i "}" %format <$> = "\mathbin{\mathopen{\langle}\$\mathclose{\rangle}}" %format Either a b = a "+" b %format fmap = "\mathsf{fmap}" %format NA = "\textsc{na}" %format NB = "\textbf{NB}" %format inT = "\mathsf{in}" %format outT = "\mathsf{out}" %format outLTree = "\mathsf{out}_{\tiny\ \textit{LTree}}" %format inLTree = "\mathsf{in}_{\tiny\ \textit{LTree}}" %format inFTree = "\mathsf{in}_{\tiny\ \textit{FTree}}" %format outFTree = "\mathsf{out}_{\tiny\ \textit{FTree}}" %format inExp = "\mathsf{in}_{\tiny\ \textit{Exp}}" %format outExp = "\mathsf{out}_{\tiny\ \textit{Exp}}" %format Null = "1" %format (Prod (a) (b)) = a >< b %format fF = "\fun F " %format l2 = "l_2 " %format Dist = "\fun{Dist}" %format IO = "\fun{IO}" %format LTree = "{\LTree}" %format FTree = "{\FTree}" %format inNat = "\mathsf{in}" %format (cata (f)) = "\llparenthesis\, " f "\,\rrparenthesis" %format (cataNat (g)) = "\llparenthesis\, " g "\,\rrparenthesis" %format (cataList (g)) = "\llparenthesis\, " g "\,\rrparenthesis" %format (cataLTree (x)) = "\llparenthesis\, " x "\,\rrparenthesis" %format (cataFTree (x)) = "\llparenthesis\, " x "\,\rrparenthesis" %format (cataRose (x)) = "\llparenthesis\, " x "\,\rrparenthesis_\textit{\tiny R}" %format (cataExp (x)) = "\llparenthesis\, " x "\,\rrparenthesis_\textit{\tiny Exp}" %format (ana (g)) = "\ana{" g "}" %format (anaList (g)) = "\anaList{" g "}" %format (anaLTree (g)) = "\lanabracket\;\!" g "\;\!\ranabracket" %format (anaRose (g)) = "\lanabracket\;\!" g "\;\!\ranabracket_\textit{\tiny R}" %format (anaExp (g)) = "\lanabracket\;\!" g "\;\!\ranabracket_\textit{\tiny Exp}" %format (hylo (g) (h)) = "\llbracket\, " g ",\," h "\,\rrbracket" %format (hyloRose (g) (h)) = "\llbracket\, " g ",\," h "\,\rrbracket_\textit{\tiny R}" %format (hyloExp (g) (h)) = "\llbracket\, " g ",\," h "\,\rrbracket_\textit{\tiny Exp}" %format Nat0 = "\N_0" %format Rational = "\Q " %format toRational = " to_\Q " %format fromRational = " from_\Q " %format muB = "\mu " %format (frac (n)(m)) = "\frac{" n "}{" m "}" %format (fac (n)) = "{" n "!}" %format (underbrace (t) (p)) = "\underbrace{" t "}_{" p "}" %format matrix = "matrix " %format `ominus` = "\mathbin{\ominus}" %format <-> = "{\,\leftrightarrow\,}" %format <|> = "{\,\updownarrow\,}" %format `minusNat`= "\mathbin{-}" %format ==> = "\Rightarrow" %format .==>. = "\Rightarrow" %format .<==>. = "\Leftrightarrow" %format .==. = "\equiv" %format .<=. = "\leq" %format .&&&. = "\wedge" %format cdots = "\cdots " %format pi = "\pi " %format (curry (f)) = "\overline{" f "}" %format delta = "\Delta " %format (plus (f)(g)) = "{" f "}\plus{" g "}" %format ++ = "\mathbin{+\!\!\!+}" %format Integer = "\mathbb{Z}" %format (Cp.cond (p) (f) (g)) = "\mcond{" p "}{" f "}{" g "}" \def\plus{\mathbin{\dagger}} %format square (x) = x "^2" %format a1 = "a_1 " %format a2 = "a_2 " %format a3 = "a_3 " %format a4 = "a_4 " %format b1 = "b_1 " %format b2 = "b_2 " %format b3 = "b_3 " %format b4 = "b_4 " %format c1 = "c_1 " %format c2 = "c_2 " %format c3 = "c_3 " %format c4 = "c_4 " %format d1 = "d_1 " %format d2 = "d_2 " %format d3 = "d_3 " %format d4 = "d_4 " %format r1 = "r_1 " %format r2 = "r_2 " %format s1 = "s_1 " %format s2 = "s_2 " %format e1 = "e_1 " %format e2 = "e_2 " \def\kcomp{\mathbin{\bullet}} %format (kcomp (f) (g)) = f "\kcomp " g %format .! = "\kcomp" %--------------------------------------------------------------------------- \title{ \textbf{Cálculo de Programas} \\ Trabalho Prático \\ LEI --- 2022/23 } \author{ \dium \\ Universidade do Minho } \date\mydate \makeindex \newcommand{\rn}[1]{\textcolor{Red}{#1}} \begin{document} \emergencystretch 3em %\sloppy \maketitle \begin{center}\large \begin{tabular}{ll} Grupo nr. & 51 \\\hline a93248 & João Tiago Alves Fidalgo de Sousa \end{tabular} \end{center} \section*{Preâmbulo} \CP\ tem como objectivo principal ensinar a progra\-mação de computadores como uma disciplina científica. Para isso parte-se de um repertório de \emph{combinadores} que formam uma álgebra da programação (conjunto de leis universais e seus corolários) e usam-se esses combinadores para construir programas \emph{composicionalmente}, isto é, agregando programas já existentes. Na sequência pedagógica dos planos de estudo dos cursos que têm esta disciplina, opta-se pela aplicação deste método à programação em \Haskell\ (sem prejuízo da sua aplicação a outras linguagens funcionais). Assim, o presente trabalho prático coloca os alunos perante problemas concretos que deverão ser implementados em \Haskell. Há ainda um outro objectivo: o de ensinar a documentar programas, a validá-los e a produzir textos técnico-científicos de qualidade. Antes de abodarem os problemas propostos no trabalho, os grupos devem ler com atenção o anexo \ref{sec:documentacao} onde encontrarão as instruções relativas ao sofware a instalar, etc. %if False \begin{code} {-# OPTIONS_GHC -XNPlusKPatterns #-} {-# LANGUAGE GeneralizedNewtypeDeriving, DeriveDataTypeable, FlexibleInstances #-} module Main where import Cp import List hiding (fac) import NEList (out) import Exp import Nat hiding (add_pair) import LTree import Rose hiding (g) import Probability import Data.List hiding (find) import Data.List.Split hiding (split,chunksOf) import Svg hiding (for,wrap) import Control.Concurrent import Cp2223data main = undefined instance Strong Dist \end{code} %endif \Problema Suponha-se uma sequência numérica semelhante à sequência de Fibonacci tal que cada termo subsequente aos três primeiros corresponde à soma dos três anteriores, sujeitos aos coeficientes |a|, |b| e |c|: \begin{code} f a b c 0 = 0 f a b c 1 = 1 f a b c 2 = 1 f a b c (n+3) = a * f a b c (n+2) + b * f a b c (n+1) + c * f a b c n \end{code} Assim, por exemplo, |f 1 1 1| irá dar como resultado a sequência: \begin{spec} 1, 1, 2, 4, 7, 13, 24, 44, 81, 149, ... \end{spec} |f 1 2 3| irá gerar a sequência: \begin{spec} 1,1,3,8,17,42,100,235,561,1331, ... \end{spec} etc. A definição de |f| dada é muito ineficiente, tendo uma degradação do tempo de execução exponencial. Pretende-se otimizar a função dada convertendo-a para um ciclo \textit{for}. Recorrendo à lei de recursividade mútua, calcule |loop| e |initial| em \begin{code} fbl a b c = wrap . (for ((loop a b c)) initial) \end{code} por forma a |f| e |fbl| serem (matematicamente) a mesma função. Para tal, poderá usar a regra prática explicada no anexo \ref{sec:mr}. \textbf{Valorização}: apresente testes de \textit{performance} que mostrem quão mais rápida é |fbl| quando comparada com |f|. \Problema Pretende-se vir a classificar os conteúdos programáticos de todas as \href{https://web.di.uminho.pt/sitedi/ucs/}{UCs} lecionadas no \dium\ de acordo com o \href{https://dl.acm.org/ccs}{ACM Computing Classification System}. A listagem da taxonomia desse sistema está disponível no ficheiro \texttt{Cp2223data}, começando com \begin{spec} acm_ccs = [ "CCS", " General and reference", " Document types", " Surveys and overviews", " Reference works", " General conference proceedings", " Biographies", " General literature", " Computing standards, RFCs and guidelines", " Cross-computing tools and techniques", \end{spec} (10 primeiros ítens) etc., etc.\footnote{Informação obtida a partir do site \href{https://dl.acm.org/ccs}{ACM CCS} selecionando \emph{Flat View}.} Pretende-se representar a mesma informação sob a forma de uma árvore de expressão, usando para isso a biblioteca \Exp\ que consta do material padagógico da disciplina e que vai incluída no zip do projecto, por ser mais conveniente para os alunos. \begin{enumerate} \item Comece por definir a função de conversão do texto dado em |acm_ccs| (uma lista de \emph{strings}) para uma tal árvore como um anamorfismo de \Exp: % \begin{code} tax :: [String] -> Exp String String tax = anaExp gene \end{code} Ou seja, defina o |gene| do anamorfismo, tendo em conta o seguinte diagrama\footnote{|S| abrevia |String|.}: \begin{eqnarray*} \xymatrix{ |Exp S S| & & S + S \times (|Exp S S|)^*\ar[ll]_{|inExp|} \\ S^*\ar@@/_1.5pc/[rr]_{|gene|}\ar[r]^(0.35){|out|}\ar[u]^{|tax|} & S + S \times S^*\ar[r]^(0.45){\cdots} & S + S \times (S^*)^*\ar[u]_{id + id \times tax^*} } \end{eqnarray*} Para isso, tome em atenção que cada nível da hierarquia é, em |acm_ccs|, marcado pela indentação de 4 espaços adicionais --- como se mostra no fragmento acima. Na figura \ref{fig:P1} mostra-se a representação gráfica da árvore de tipo \Exp\ que representa o fragmento de |acm_ccs| mostrado acima. \begin{figure}[ht!] \centering \begin{tikzpicture} [-,every node/.style={shape=rectangle,inner sep=3pt,draw}] \footnotesize \node {CSS} [edge from parent fork down] [sibling distance=4cm] child {node [align=center] {General and\\reference} [sibling distance=4cm] child {node {Document types} [sibling distance=2.25cm] child {node [align=center] {Surveys and\\overviews}} child {node [align=center] {Reference\\works}} child {node [align=center] {General\\conference\\proceedings}} child {node [align=center] {Biographies}} child {node [align=center] {General\\literature}} child {node [align=center, xshift=0.75cm] {Computing standards,\\RFCs and\\guidelines}} } child {node [align=center] {Cross-computing tools and\\techniques}} } ; \end{tikzpicture} \caption{Fragmento de |acm_ccs| representado sob a forma de uma árvore do tipo \Exp.} \label{fig:P1} \end{figure} \item De seguida vamos querer todos os caminhos da árvore que é gerada por |tax|, pois a classificação de uma UC pode ser feita a qualquer nível (isto é, caminho descendente da raiz |"CCS"| até um subnível ou folha). \footnote{Para um exemplo de classificação de UC concreto, pf.\ ver a secção \textbf{Classificação ACM} na página pública de \CP.} Precisamos pois da composição de |tax| com uma função de pós-processamento |post|, % \begin{spec} tudo :: [String] -> [[String]] tudo = post . tax \end{spec} para obter o efeito que se mostra na tabela \ref{table:acmccs}. \begin{table}[ht!] \centering\small \begin{center} \begin{tabular}{||l||l||l||l||} \hline CCS & & & \\\hline CCS & General and reference & & \\\hline CCS & General and reference & Document types & \\\hline CCS & General and reference & Document types & Surveys and overviews \\\hline CCS & General and reference & Document types & Reference works \\\hline CCS & General and reference & Document types & General conference proceedings \\\hline CCS & General and reference & Document types & Biographies \\\hline CCS & General and reference & Document types & General literature \\\hline CCS & General and reference & Cross-computing tools and techniques & \\\hline \end{tabular} \end{center} \caption{Taxonomia ACM fechada por prefixos (10 primeiros ítens).} \label{table:acmccs} \end{table} Defina a função |post :: Exp String String -> [[String]]| da forma mais económica que encontrar. \end{enumerate} \textbf{Sugestão}: Inspecione as bibliotecas fornecidas à procura de funções auxiliares que possa re-utilizar para a sua solução ficar mais simples. Não se esqueça que, para o mesmo resultado, nesta disciplina \emph{``ganha'' quem escrever menos código}! \textbf{Sugestão}: Para efeitos de testes intermédios não use a totalidade de |acm_ccs|, que tem 2114 linhas! Use, por exemplo, |take 10 acm_ccs|, como se mostrou acima. \Problema O \sierpCarpet{tapete de Sierpinski} é uma figura geométrica \fractal\ em que um quadrado é subdividido recursivamente em sub-quadrados. A construção clássica do tapete de Sierpinski é a seguinte: assumindo um quadrado de lado |l|, este é subdivido em 9 quadrados iguais de lado |l / 3|, removendo-se o quadrado central. Este passo é depois repetido sucessivamente para cada um dos 8 sub-quadrados restantes (Fig.~\ref{fig:sierp1}). \begin{figure}[h!] \centering \includegraphics[width=0.19\textwidth]{cp2223t_media/tapete1.png} \includegraphics[width=0.19\textwidth]{cp2223t_media/tapete2.png} \includegraphics[width=0.19\textwidth]{cp2223t_media/tapete3.png} \includegraphics[width=0.19\textwidth]{cp2223t_media/tapete4.png} \includegraphics[width=0.19\textwidth]{cp2223t_media/tapete5.png} \caption{Construção do tapete de Sierpinski com profundidade 5.} \label{fig:sierp1} \end{figure} \noindent |NB|: No exemplo da fig.~\ref{fig:sierp1}, assumindo a construção clássica já referida, os quadrados estão a branco e o fundo a verde. A complexidade deste algoritmo, em função do número de quadrados a desenhar, para uma profundidade $n$, é de $8^n$ (exponencial). No entanto, se assumirmos que os quadrados a desenhar são os que estão a verde, a complexidade é reduzida para $\sum_{i=0}^{n-1}8^i$, obtendo um ganho de $\sum_{i=1}^{n} \frac{100}{8^i} \%$. Por exemplo, para $n=5$, o ganho é de $14.28 \%$. O objetivo deste problema é a implementação do algoritmo mediante a referida otimização. \begin{figure}[h!] \centering \includegraphics[width=0.35\textwidth]{cp2223t_media/tapete_ex} \caption{Tapete de Sierpinski com profundidade 2 e com os quadrados enumerados.} \label{fig:sierp2} \end{figure} Assim, seja cada quadrado descrito geometricamente pelas coordenadas do seu vértice inferior esquerdo e o comprimento do seu lado: \begin{code} type Square = (Point, Side) type Side = Double type Point = (Double, Double) \end{code} A estrutura recursiva de suporte à construção de tapetes de Sierpinski será uma \Rose, na qual cada nível da árvore irá guardar os quadrados de tamanho igual. Por exemplo, a construção da fig.~\ref{fig:sierp2} poderá\footnote{A ordem dos filhos não é relevante.} corresponder à árvore da figura \ref{fig:roseTreeSierp}. \begin{figure}[ht!] \centering \begin{tikzpicture} [level distance = 2cm, level 1/.style = {sibling distance = 1.5cm}, level 2/.style = {sibling distance = 0.9cm}, ]\node [draw, circle]{1} child {node [draw, circle]{2} child {node [draw, circle]{10}} child {node [draw, circle]{11}} child {node [draw, circle]{12}} child {node [draw, circle]{13}} child {node [draw, circle]{14}} child {node [draw, circle]{15}} child {node [draw, circle]{16}} child {node [draw, circle]{17}}} child {node [draw, circle]{3}} child {node [draw, circle]{4}} child {node [draw, circle]{5}} child {node [draw, circle]{6}} child {node [draw, circle]{7}} child {node [draw, circle]{8}} child {node [draw, circle]{9}}; \end{tikzpicture} \caption{Possível árvore de suporte para a construção da fig.~\ref{fig:sierp2}.} \label{fig:roseTreeSierp} \end{figure} Uma vez que o tapete é também um quadrado, o objetivo será, a partir das informações do tapete (coordenadas do vértice inferior esquerdo e comprimento do lado), desenhar o quadrado central, subdividir o tapete nos 8 sub-tapetes restantes, e voltar a desenhar, recursivamente, o quadrado nesses 8 sub-tapetes. Desta forma, cada tapete determina o seu quadrado e os seus 8 sub-tapetes. No exemplo em cima, o tapete que contém o quadrado 1 determina esse próprio quadrado e determina os sub-tapetes que contêm os quadrados 2 a 9. Portanto, numa primeira fase, dadas as informações do tapete, é construida a árvore de suporte com todos os quadrados a desenhar, para uma determinada profundidade. \begin{code} squares :: (Square, Int) -> Rose Square \end{code} |NB|: No programa, a profundidade começa em $0$ e não em $1$. Uma vez gerada a árvore com todos os quadrados a desenhar, é necessário extrair os quadrados para uma lista, a qual é processada pela função |drawSq|, disponibilizada no anexo \ref{sec:codigo}. \begin{code} rose2List :: Rose a -> [a] \end{code} Assim, a construção de tapetes de Sierpinski é dada por um hilomorfismo de \textit{Rose Trees}: \begin{code} sierpinski :: (Square, Int) -> [Square] sierpinski = hyloRose gr2l gsq \end{code} \textbf{Trabalho a fazer:} \begin{enumerate} \item Definir os genes do hilomorfismo |sierpinski|. \item Correr \begin{code} sierp4 = drawSq (sierpinski (((0,0),32),3)) constructSierp5 = do drawSq (sierpinski (((0,0),32),0)) await drawSq (sierpinski (((0,0),32),1)) await drawSq (sierpinski (((0,0),32),2)) await drawSq (sierpinski (((0,0),32),3)) await drawSq (sierpinski (((0,0),32),4)) await \end{code} \item Definir a função que apresenta a construção do tapete de Sierpinski como é apresentada em |construcaoSierp5|, mas para uma profundidade $n \in \mathbb{N}$ recebida como parâmetro. \begin{code} constructSierp :: Int -> IO [()] constructSierp = present . carpets \end{code} \textbf{Dica}: a função |constructSierp| será um hilomorfismo de listas, cujo anamorfismo |carpets :: Int -> [[Square]]| constrói, recebendo como parâmetro a profundidade $n$, a lista com todos os tapetes de profundidade $1..n$, e o catamorfismo |present :: [[Square]] -> IO [()]| percorre a lista desenhando os tapetes e esperando 1 segundo de intervalo. \end{enumerate} \Problema Este ano ocorrerá a vigésima segunda edição do Campeonato do Mundo de Futebol, organizado pela Federação Internacional de Futebol (FIFA), a decorrer no Qatar e com o jogo inaugural a 20 de Novembro. Uma casa de apostas pretende calcular, com base numa aproximação dos \textit{rankings}\footnote{Os \textit{rankings} obtidos \href{https://www.fifa.com/fifa-world-ranking/men?dateId=id13687}{aqui} foram escalados e arredondados.} das seleções, a probabilidade de cada seleção vencer a competição. Para isso, o diretor da casa de apostas contratou o Departamento de Informática da Universidade do Minho, que atribuiu o projeto à equipa formada pelos alunos e pelos docentes de Cálculo de Programas. \begin{alert} Para resolver este problema de forma simples, ele será abordado por duas fases: \begin{enumerate} \item versão académica sem probabilidades, em que se sabe à partida, num jogo, quem o vai vencer; \item versão realista com probabilidades usando o mónade \textit{Dist} (distribuições probabilísticas) que vem descrito no anexo \ref{sec:probabilities}. \end{enumerate} A primeira versão, mais simples, deverá ajudar a construir a segunda. \end{alert} \subsection*{Descrição do problema} Uma vez garantida a qualificação (já ocorrida), o campeonato consta de duas fases consecutivas no tempo: \begin{enumerate} \item fase de grupos; \item fase eliminatória (ou ``mata-mata'', como é habitual dizer-se no Brasil). \end{enumerate} Para a fase de grupos, é feito um sorteio das 32 seleções (o qual já ocorreu para esta competição) que as coloca em 8 grupos, 4 seleções em cada grupo. Assim, cada grupo é uma lista de seleções. Os grupos para o campeonato deste ano são: \begin{code} type Team = String type Group = [Team] groups :: [Group] groups = [["Qatar", "Ecuador", "Senegal", "Netherlands"], ["England", "Iran", "USA", "Wales"], ["Argentina", "Saudi Arabia", "Mexico", "Poland"], ["France", "Denmark", "Tunisia", "Australia"], ["Spain", "Germany", "Japan", "Costa Rica"], ["Belgium", "Canada", "Morocco", "Croatia"], ["Brazil", "Serbia", "Switzerland", "Cameroon"], ["Portugal", "Ghana", "Uruguay", "Korea Republic"]] \end{code} Deste modo, \textit{groups !! 0} corresponde ao grupo A, \textit{groups !! 1} ao grupo B, e assim sucessivamente. Nesta fase, cada seleção de cada grupo vai defrontar (uma vez) as outras do seu grupo. Passam para o ``mata-mata'' as duas seleções que mais pontuarem em cada grupo, obtendo pontos, por cada jogo da fase grupos, da seguinte forma: \begin{itemize} \item vitória --- 3 pontos; \item empate --- 1 ponto; \item derrota --- 0 pontos. \end{itemize} Como se disse, a posição final no grupo irá determinar se uma seleção avança para o ``mata-mata'' e, se avançar, que possíveis jogos terá pela frente, uma vez que a disposição das seleções está desde o início definida para esta última fase, conforme se pode ver na figura \ref{fig:wcup22}. \begin{figure}[ht] \centering \includegraphics[scale=0.125]{cp2223t_media/wcup2022.png} \caption{O ``mata-mata''} \label{fig:wcup22} \end{figure} Assim, é necessário calcular os vencedores dos grupos sob uma distribuição probabilística. Uma vez calculadas as distribuições dos vencedores, é necessário colocá-las nas folhas de uma |LTree| de forma a fazer um \textit{match} com a figura \ref{fig:wcup22}, entrando assim na fase final da competição, o tão esperado ``mata-mata''. Para avançar nesta fase final da competição (i.e.\ subir na árvore), é preciso ganhar, quem perder é automaticamente eliminado (``mata-mata''). Quando uma seleção vence um jogo, sobe na árvore, quando perde, fica pelo caminho. Isto significa que a seleção vencedora é aquela que vence todos os jogos do ``mata-mata''. \subsection*{Arquitetura proposta} A visão composicional da equipa permitiu-lhe perceber desde logo que o problema podia ser dividido, independentemente da versão, probabilística ou não, em duas partes independentes --- a da fase de grupos e a do ``mata-mata''. Assim, duas sub-equipas poderiam trabalhar em paralelo, desde que se garantisse a composicionalidade das partes. Decidiu-se que os alunos desenvolveriam a parte da fase de grupos e os docentes a do ``mata-mata''. \subsubsection*{Versão não probabilística} O resultado final (não probabilístico) é dado pela seguinte função: \begin{code} winner :: Team winner = wcup groups wcup = knockoutStage . groupStage \end{code} A sub-equipa dos docentes já entregou a sua parte: \begin{code} knockoutStage = cataLTree (either id koCriteria) \end{code} Considere-se agora a proposta do \textit{team leader} da sub-equipa dos alunos para o desenvolvimento da fase de grupos: \begin{bquote} {\slshape Vamos dividir o processo em 3 partes: \begin{itemize} \item gerar os jogos, \item simular os jogos, \item preparar o ``mata-mata'' gerando a árvore de jogos dessa fase (fig. \ref{fig:wcup22}). \end{itemize} Assim: \begin{code} groupStage :: [Group] -> LTree Team groupStage = initKnockoutStage . simulateGroupStage . genGroupStageMatches \end{code} Comecemos então por definir a função |genGroupStageMatches| que gera os jogos da fase de grupos: \begin{code} genGroupStageMatches :: [Group] -> [[Match]] genGroupStageMatches = map generateMatches \end{code} onde \begin{code} type Match = (Team, Team) \end{code} Ora, sabemos que nos foi dada a função \begin{code} gsCriteria :: Match -> Maybe Team \end{code} que, mediante um certo critério, calcula o resultado de um jogo, retornando |Nothing| em caso de empate, ou a equipa vencedora (sob o construtor |Just|). Assim, precisamos de definir a função \begin{code} simulateGroupStage :: [[Match]] -> [[Team]] simulateGroupStage = map (groupWinners gsCriteria) \end{code} que simula a fase de grupos e dá como resultado a lista dos vencedores, recorrendo à função |groupWinners|: \begin{code} groupWinners criteria = best 2 . consolidate . (>>= matchResult criteria) \end{code} Aqui está apenas em falta a definição da função |matchResult|. Por fim, teremos a função |initKnockoutStage| que produzirá a |LTree| que a sub-equipa do ``mata-mata'' precisa, com as devidas posições. Esta será a composição de duas funções: \begin{code} initKnockoutStage = anaLTree glt . arrangement \end{code} } \end{bquote} Trabalho a fazer: \begin{enumerate} \item Definir uma alternativa à função genérica |consolidate| que seja um catamorfismo de listas: \begin{code} consolidate' :: (Eq a, Num b) => [(a, b)] -> [(a, b)] consolidate' = cataList cgene \end{code} \item Definir a função |matchResult :: (Match -> Maybe Team) -> Match -> [(Team, Int)]| que apura os pontos das equipas de um dado jogo. \item Definir a função genérica |pairup :: Eq b => [b] -> [(b, b)]| em que |generateMatches| se baseia. \item Definir o gene |glt|. \end{enumerate} \subsubsection*{Versão probabilística} Nesta versão, mais realista, |gsCriteria :: Match -> (Maybe Team)| dá lugar a \begin{code} pgsCriteria :: Match -> Dist (Maybe Team) \end{code} que dá, para cada jogo, a probabilidade de cada equipa vencer ou haver um empate. Por exemplo, há |50%| de probabilidades de Portugal empatar com a Inglaterra, \begin{quote} \begin{verbatim} pgsCriteria("Portugal","England") Nothing 50.0% Just "England" 26.7% Just "Portugal" 23.3% \end{verbatim} \end{quote} etc. O que é |Dist|? É o mónade que trata de distribuições probabilísticas e que é descrito no anexo \ref{sec:probabilities}, página \pageref{sec:probabilities} e seguintes. O que há a fazer? Eis o que diz o vosso \emph{team leader}: \begin{bquote} \slshape O que há a fazer nesta versão é, antes de mais, avaliar qual é o impacto de |gsCriteria| virar monádica (em |Dist|) na arquitetura geral da versão anterior. Há que reduzir esse impacto ao mínimo, escrevendo-se tão pouco código quanto possível! \end{bquote} Todos relembraram algo que tinham aprendido nas aulas teóricas a respeito da ``monadificação'' do código: há que reutilizar o código da versão anterior, monadificando-o. Para distinguir as duas versões decidiu-se afixar o prefixo `p' para identificar uma função que passou a ser monádica. A sub-equipa dos docentes fez entretanto a monadificação da sua parte: \begin{spec} pwinner :: Dist Team pwinner = pwcup groups pwcup = pknockoutStage .! pgroupStage \end{spec} E entregou ainda a versão probabilística do ``mata-mata'': \begin{code} pknockoutStage = mcataLTree' (either return pkoCriteria) mcataLTree' g = k where k (Leaf a) = g1 a k (Fork(x,y)) = mmbin g2 (k x, k y) g1 = g . i1 g2 = g . i2 \end{code} A sub-equipa dos alunos também já adiantou trabalho, \begin{code} pgroupStage = pinitKnockoutStage .! psimulateGroupStage . genGroupStageMatches \end{code} mas faltam ainda |pinitKnockoutStage| e |pgroupWinners|, esta usada em |psimulateGroupStage|, que é dada em anexo. Trabalho a fazer: \begin{itemize} \item Definir as funções que ainda não estão implementadas nesta versão. \item \textbf{Valorização}: experimentar com outros critérios de ``ranking'' das equipas. \end{itemize} \begin{alert} \textbf{Importante}: (a) código adicional terá que ser colocado no anexo \ref{sec:resolucao}, obrigatoriamente; (b) todo o código que é dado não pode ser alterado. \end{alert} \part*{Anexos} \appendix \section{Documentação para realizar o trabalho} \label{sec:documentacao} Para cumprir de forma integrada os objectivos do trabalho vamos recorrer a uma técnica de programa\-ção dita ``\litp{literária}'' \cite{Kn92}, cujo princípio base é o seguinte: % \begin{quote}\em Um programa e a sua documentação devem coincidir. \end{quote} % Por outras palavras, o código fonte e a documentação de um programa deverão estar no mesmo ficheiro. O ficheiro \texttt{cp2223t.pdf} que está a ler é já um exemplo de \litp{programação literária}: foi gerado a partir do texto fonte \texttt{cp2223t.lhs}\footnote{O sufixo `lhs' quer dizer \emph{\lhaskell{literate Haskell}}.} que encontrará no \MaterialPedagogico\ desta disciplina descompactando o ficheiro \texttt{cp2223t.zip} e executando: \begin{Verbatim}[fontsize=\small] $ lhs2TeX cp2223t.lhs > cp2223t.tex $ pdflatex cp2223t \end{Verbatim} em que \href{https://hackage.haskell.org/package/lhs2tex}{\texttt\LhsToTeX} é um pré-processador que faz ``pretty printing'' de código Haskell em \Latex\ e que deve desde já instalar utilizando o utiliário \href{https://www.haskell.org/cabal/}{cabal} disponível em \href{https://www.haskell.org}{haskell.org}. Por outro lado, o mesmo ficheiro \texttt{cp2223t.lhs} é executável e contém o ``kit'' básico, escrito em \Haskell, para realizar o trabalho. Basta executar \begin{Verbatim}[fontsize=\small] $ ghci cp2223t.lhs \end{Verbatim} \noindent Abra o ficheiro \texttt{cp2223t.lhs} no seu editor de texto preferido e verifique que assim é: todo o texto que se encontra dentro do ambiente \begin{quote}\small\tt \verb!\begin{code}! \\ ... \\ \verb!\end{code}! \end{quote} é seleccionado pelo \GHCi\ para ser executado. \subsection{Como realizar o trabalho} Este trabalho teórico-prático deve ser realizado por grupos de 3 (ou 4) alunos. Os detalhes da avaliação (datas para submissão do relatório e sua defesa oral) são os que forem publicados na \cp{página da disciplina} na \emph{internet}. Recomenda-se uma abordagem participativa dos membros do grupo em todos os exercícios do trabalho, para assim poderem responder a qualquer questão colocada na \emph{defesa oral} do relatório. Em que consiste, então, o \emph{relatório} a que se refere o parágrafo anterior? É a edição do texto que está a ser lido, preenchendo o anexo \ref{sec:resolucao} com as respostas. O relatório deverá conter ainda a identificação dos membros do grupo de trabalho, no local respectivo da folha de rosto. Para gerar o PDF integral do relatório deve-se ainda correr os comando seguintes, que actualizam a bibliografia (com \Bibtex) e o índice remissivo (com \Makeindex), \begin{Verbatim}[fontsize=\small] $ bibtex cp2223t.aux $ makeindex cp2223t.idx \end{Verbatim} e recompilar o texto como acima se indicou. No anexo \ref{sec:codigo}, disponibiliza-se algum código \Haskell\ relativo aos problemas apresentados. Esse anexo deverá ser consultado e analisado à medida que isso for necessário. \subsection{Como exprimir cálculos e diagramas em LaTeX/lhs2tex} Como primeiro exemplo, estudar o texto fonte deste trabalho para obter o efeito:\footnote{Exemplos tirados de \cite{Ol18}.} \start \begin{eqnarray*} |id = split f g| % \just\equiv{ universal property } % |lcbr( p1 . id = f )( p2 . id = g )| % \just\equiv{ identity } % |lcbr( p1 = f )( p2 = g )| \qed \end{eqnarray*} Os diagramas podem ser produzidos recorrendo à \emph{package} \LaTeX\ \href{https://ctan.org/pkg/xymatrix}{xymatrix}, por exemplo: \begin{eqnarray*} \xymatrix@@C=2cm{ |Nat0| \ar[d]_-{|cataNat g|} & |1 + Nat0| \ar[d]^{|id + (cataNat g)|} \ar[l]_-{|inNat|} \\ |B| & |1 + B| \ar[l]^-{|g|} } \end{eqnarray*} \section{Regra prática para a recursividade mútua em |Nat0|}\label{sec:mr} Nesta disciplina estudou-se como fazer \pd{programação dinâmica} por cálculo, recorrendo à lei de recursividade mútua.\footnote{Lei (\ref{eq:fokkinga}) em \cite{Ol18}, página \pageref{eq:fokkinga}.} Para o caso de funções sobre os números naturais (|Nat0|, com functor |fF X = 1 + X|) é fácil derivar-se da lei que foi estudada uma \emph{regra de algibeira} \label{pg:regra} que se pode ensinar a programadores que não tenham estudado \cp{Cálculo de Programas}. Apresenta-se de seguida essa regra, tomando como exemplo o cálculo do ciclo-\textsf{for} que implementa a função de Fibonacci, recordar o sistema: \begin{spec} fib 0 = 1 fib(n+1) = f n f 0 = 1 f (n+1) = fib n + f n \end{spec} Obter-se-á de imediato \begin{code} fib' = p1 . for loop init where loop(fib,f)=(f,fib+f) init = (1,1) \end{code} usando as regras seguintes: \begin{itemize} \item O corpo do ciclo |loop| terá tantos argumentos quanto o número de funções mutuamente recursivas. \item Para as variáveis escolhem-se os próprios nomes das funções, pela ordem que se achar conveniente.\footnote{Podem obviamente usar-se outros símbolos, mas numa primeira leitura dá jeito usarem-se tais nomes.} \item Para os resultados vão-se buscar as expressões respectivas, retirando a variável |n|. \item Em |init| coleccionam-se os resultados dos casos de base das funções, pela mesma ordem. \end{itemize} Mais um exemplo, envolvendo polinómios do segundo grau $ax^2 + b x + c$ em |Nat0|. Seguindo o método estudado nas aulas\footnote{Secção 3.17 de \cite{Ol18} e tópico \href{https://www4.di.uminho.pt/~jno/media/cp/}{Recursividade mútua} nos vídeos de apoio às aulas teóricas.}, de $f\ x = a x^2 + b x + c$ derivam-se duas funções mutuamente recursivas: \begin{spec} f 0 = c f (n+1) = f n + k n k 0 = a + b k(n+1) = k n + 2 a \end{spec} Seguindo a regra acima, calcula-se de imediato a seguinte implementação, em Haskell: \begin{code} f' a b c = p1 . for loop init where loop(f,k) = (f+k,k+2*a) init = (c,a+b) \end{code} \section{O mónade das distribuições probabilísticas} \label{sec:probabilities} %format B = "\mathit B" %format C = "\mathit C" Mónades são functores com propriedades adicionais que nos permitem obter efeitos especiais em progra\-mação. Por exemplo, a biblioteca \Probability\ oferece um mónade para abordar problemas de probabilidades. Nesta biblioteca, o conceito de distribuição estatística é captado pelo tipo \begin{eqnarray} |newtype Dist a = D {unD :: [(a, ProbRep)]}| \label{eq:Dist} \end{eqnarray} em que |ProbRep| é um real de |0| a |1|, equivalente a uma escala de $0$ a $100 \%$. Cada par |(a,p)| numa distribuição |d::Dist a| indica que a probabilidade de |a| é |p|, devendo ser garantida a propriedade de que todas as probabilidades de |d| somam $100\%$. Por exemplo, a seguinte distribuição de classificações por escalões de $A$ a $E$, \[ \begin{array}{ll} A & \rule{2mm}{3pt}\ 2\%\\ B & \rule{12mm}{3pt}\ 12\%\\ C & \rule{29mm}{3pt}\ 29\%\\ D & \rule{35mm}{3pt}\ 35\%\\ E & \rule{22mm}{3pt}\ 22\%\\ \end{array} \] será representada pela distribuição \begin{code} d1 :: Dist Char d1 = D [('A',0.02),('B',0.12),('C',0.29),('D',0.35),('E',0.22)] \end{code} que o \GHCi\ mostrará assim: \begin{Verbatim}[fontsize=\small] 'D' 35.0% 'C' 29.0% 'E' 22.0% 'B' 12.0% 'A' 2.0% \end{Verbatim} É possível definir geradores de distribuições, por exemplo distribuições \emph{uniformes}, \begin{code} d2 = uniform (words "Uma frase de cinco palavras") \end{code} isto é \begin{Verbatim}[fontsize=\small] "Uma" 20.0% "cinco" 20.0% "de" 20.0% "frase" 20.0% "palavras" 20.0% \end{Verbatim} distribuição \emph{normais}, eg.\ \begin{code} d3 = normal [10..20] \end{code} etc.\footnote{Para mais detalhes ver o código fonte de \Probability, que é uma adaptação da biblioteca \PFP\ (``Probabilistic Functional Programming''). Para quem quiser saber mais recomenda-se a leitura do artigo \cite{EK06}.} |Dist| forma um \textbf{mónade} cuja unidade é |return a = D [(a,1)]| e cuja composição de Kleisli é (simplificando a notação) \begin{spec} ((kcomp f g)) a = [(y,q*p) | (x,p) <- g a, (y,q) <- f x] \end{spec} em que |g: A -> Dist B| e |f: B -> Dist C| são funções \textbf{monádicas} que representam \emph{computações probabilísticas}. Este mónade é adequado à resolução de problemas de \emph{probabilidades e estatística} usando programação funcional, de forma elegante e como caso particular da programação monádica. \section{Código fornecido}\label{sec:codigo} \subsection*{Problema 1} Alguns testes para se validar a solução encontrada: \begin{code} test a b c = map (fbl a b c) x == map (f a b c) x where x = [1..20] test1 = test 1 2 3 test2 = test (-2) 1 5 \end{code} \subsection*{Problema 2} \textbf{Verificação}: a árvore de tipo \Exp\ gerada por \begin{code} acm_tree = tax acm_ccs \end{code} deverá verificar as propriedades seguintes: \begin{itemize} \item |expDepth acm_tree == 7| (profundidade da árvore); \item |length (expOps acm_tree) == 432| (número de nós da árvore); \item |length (expLeaves acm_tree) == 1682| (número de folhas da árvore).\footnote{Quer dizer, o número total de nodos e folhas é |2114|, o número de linhas do texto dado.} \end{itemize} O resultado final \begin{code} acm_xls = post acm_tree \end{code} não deverá ter tamanho inferior ao total de nodos e folhas da árvore. \subsection*{Problema 3} Função para visualização em \svg: \begin{code} drawSq x = picd'' [ Svg.scale 0.44 (0,0) (x >>= sq2svg) ] sq2svg (p,l) = (color "#67AB9F" . polyg) [ p, p .+ (0,l), p .+ (l,l), p .+ (l,0) ] \end{code} Para efeitos de temporização: \begin{code} await = threadDelay 1000000 \end{code} \subsection*{Problema 4} Rankings: \begin{code} rankings = [ ("Argentina",4.8), ("Australia",4.0), ("Belgium",5.0), ("Brazil",5.0), ("Cameroon",4.0), ("Canada",4.0), ("Costa Rica",4.1), ("Croatia",4.4), ("Denmark",4.5), ("Ecuador",4.0), ("England",4.7), ("France",4.8), ("Germany",4.5), ("Ghana",3.8), ("Iran",4.2), ("Japan",4.2), ("Korea Republic",4.2), ("Mexico",4.5), ("Morocco",4.2), ("Netherlands",4.6), ("Poland",4.2), ("Portugal",4.6), ("Qatar",3.9), ("Saudi Arabia",3.9), ("Senegal",4.3), ("Serbia",4.2), ("Spain",4.7), ("Switzerland",4.4), ("Tunisia",4.1), ("USA",4.4), ("Uruguay",4.5), ("Wales",4.3)] \end{code} Geração dos jogos da fase de grupos: \begin{code} generateMatches = pairup \end{code} Preparação da árvore do ``mata-mata'': \begin{code} arrangement = (>>= swapTeams) . chunksOf 4 where swapTeams [[a1,a2],[b1,b2],[c1,c2],[d1,d2]] = [a1,b2,c1,d2,b1,a2,d1,c2] \end{code} Função proposta para se obter o \emph{ranking} de cada equipa: \begin{code} rank x = 4 ** (pap rankings x - 3.8) \end{code} Critério para a simulação não probabilística dos jogos da fase de grupos: \begin{code} gsCriteria = s . split (id >< id) (rank >< rank) where s ((s1, s2), (r1, r2)) = let d = r1 - r2 in if d > 0.5 then Just s1 else if d < -0.5 then Just s2 else Nothing \end{code} Critério para a simulação não probabilística dos jogos do mata-mata: \begin{code} koCriteria = s . split (id >< id) (rank >< rank) where s ((s1, s2), (r1, r2)) = let d = r1 - r2 in if d == 0 then s1 else if d > 0 then s1 else s2 \end{code} Critério para a simulação probabilística dos jogos da fase de grupos: \begin{code} pgsCriteria = s . split (id >< id) (rank >< rank) where s ((s1, s2), (r1, r2)) = if abs(r1-r2) > 0.5 then fmap Just (pkoCriteria(s1,s2)) else f (s1,s2) f = D . ((Nothing,0.5):) . map (Just><(/2)) . unD . pkoCriteria \end{code} Critério para a simulação probabilística dos jogos do mata-mata: \begin{code} pkoCriteria (e1, e2) = D [(e1, 1 - r2 / (r1 + r2)), (e2, 1 - r1 / (r1 + r2))] where r1 = rank e1 r2 = rank e2 \end{code} Versão probabilística da simulação da fase de grupos:\footnote{Faz-se ``trimming'' das distribuições para reduzir o tempo de simulação.} \begin{code} psimulateGroupStage = trim . map (pgroupWinners pgsCriteria) trim = top 5 . sequence . map (filterP.norm) where filterP (D x) = D [(a,p) | (a,p) <- x, p > 0.0001 ] top n = vec2Dist . take n . reverse . presort snd . unD vec2Dist x = D [ (a,n/t) | (a,n) <- x ] where t = sum(map snd x) \end{code} Versão mais eficiente da |pwinner| dada no texto principal, para diminuir o tempo de cada simulação: \begin{code} pwinner :: Dist Team pwinner = mbin f x >>= pknockoutStage where f(x,y) = initKnockoutStage(x++y) x = split (g . take 4) (g . drop 4) groups g = psimulateGroupStage . genGroupStageMatches \end{code} Auxiliares: \begin{code} best n = map fst . take n . reverse . presort p2 consolidate :: (Num d, Eq d, Eq b) => [(b, d)] -> [(b, d)] consolidate = map (id> [(a, b)] -> [(a, [b])] collect x = nub [ k |-> [ d' | (k',d') <- x , k'==k ] | (k,d) <- x ] \end{code} Função binária monádica |f|: \begin{code} mmbin :: Monad m => ((a, b) -> m c) -> (m a, m b) -> m c mmbin f (a,b) = do { x <- a ; y <- b ; f(x,y) } \end{code} Monadificação de uma função binária |f|: \begin{code} mbin :: Monad m => ((a, b) -> c) -> (m a, m b) -> m c mbin = mmbin . (return.) \end{code} Outras funções que podem ser úteis: \begin{code} (f `is` v) x = (f x) == v rcons(x,a) = x++[a] \end{code} %----------------- Soluções dos alunos -----------------------------------------% \section{Soluções dos alunos}\label{sec:resolucao} Os alunos devem colocar neste anexo as suas soluções para os exercícios propostos, de acordo com o ``layout'' que se fornece. Não podem ser alterados os nomes ou tipos das funções dadas, mas pode ser adicionado texto, diagramas e/ou outras funções auxiliares que sejam necessárias. Valoriza-se a escrita de \emph{pouco} código que corresponda a soluções simples e elegantes. \subsection*{Problema 1} Partindo da função inicial: \begin{spec} f a b c 0 = 0 f a b c 1 = 1 f a b c 2 = 1 f a b c (n+3) = a * f a b c (n+2) + b * f a b c (n+1) + c * f a b c n \end{spec} Igualando: \begin{spec} g a b c 0 = f a b c (0+2) g a b c (n+1) = f a b c (n+3) \end{spec} Obtemos: \begin{spec} f a b c 0 = 0 f a b c 1 = 1 f a b c (n+2) = g a b c n g a b c 0 = 1 g a b c (n+1) = a * g a b c n + b * f a b c (n+1) + c * f a b c n \end{spec} Seguindo a mesma lógica usada anteriormente: \begin{spec} h a b c 0 = f a b c (0+1) h a b c (n+1) = f a b c (n+2) \end{spec} E obtemos: \begin{spec} f a b c 0 = 0 f a b c (n+1) = h a b c n g a b c 0 = 1 g a b c (n+1) = a * g a b c n + b * h a b c n + c * f a b c n h a b c 0 = 1 h a b c (n+1) = g a b c n \end{spec} Reorganizando as funções devido ao wrapper do loop ser |p2| \begin{spec} g a b c 0 = 1 g a b c (n+1) = a * g a b c n + b * h a b c n + c * f a b c n h a b c 0 = 1 h a b c (n+1) = g a b c n f a b c 0 = 0 f a b c (n+1) = h a b c n \end{spec} Aplicando a regra prática explicada no anexo \ref{sec:mr}, obtemos a solução Funções auxiliares pedidas: \begin{code} loop a b c ((g, h), f) = (((a * g + b * h + c * f), g), h) initial = ((1,1),0) wrap = p2 \end{code} \subsection*{Problema 2} Gene de |tax|: \begin{eqnarray*} \xymatrix@@C=2cm{ |Exp S S| & S + S \times (|Exp S S|)^* \ar[l]_-{|inExp|} \\ S^* \ar[u]^-{tax} \ar[r]_-{gene} & S + S \times (S^*)^* \ar[u]_{id + id \times |tax|^*} } \end{eqnarray*} \begin{code} gene = (id -|- (id >< (groupBy (\x y -> countSpaces y > 0) . map (drop 4)))) . out where countSpaces = length . takeWhile (== ' ') \end{code} % explicacao do gene Em primeiro lugar aplico à lista de input o |out| das listas não vazias, de seguida conservo o elemento singular, e a cabeça aplicando a ambos id. Para a cauda da lista removo 4 espaços de todos os elementos para poder distinguir os precedentes dos descendentes. Finalmente agrupo os elementos da lista, por número de espaços á cabeça (caso o número de espaços seja 0, indica que esse elemento é o "pai" dessa lista), em várias listas, cujo primeiro elemento é o "pai" dos restantes. % ---------- Problema 2 Post --------------- Função de pós-processamento: \begin{code} post :: Exp String String -> [[String]] post = cataExp (gene_post) gene_post = either leftSide rightSide leftSide = singl . singl rightSide = cons . (split (singl . p1) ((map cons) . lstr . (id >< concat))) \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ |Exp S S| \ar[d]_-{|post|} \ar[r]^-{|outExp|} & S + S \times (|Exp S S|)^* \ar[d]^-{id + id \times |post|^*} \\ (S^*)^* & S + S \times ((S^*)^*)^* \ar[l]^-{|gene_post|} \ar[d]^-{|singl| + |split (singl . p1) ((map cons) . lstr . (id >< concat))|} \\ & S^* + S^* \times (S^*)^* \ar[ul]^-{|either singl cons|} } \end{eqnarray*} Desenhando o diagrama do catamorfismo de árvores |Exp|, a partir do seu funtor, descobri o tipo de entrada do gene. Tendo isto em mente, comecei por analisar qual seria o significado, para este contexto, do tipo de entrada, e cheguei à conclusão de que o lado esquerdo da alternativa, representa o conteúdo de uma folha da àrvore, logo será um elemento sem descentes. Para este caso apenas tenho de aplicar |singl . singl|. Do lado direito da alternativa, o primeiro elemento do par representa o "pai" de todos os elementos do segundo elemento par, que contém os resultados das chamadas recursivas. Tendo isso em conta, conservo e crio lista com o "pai", e uma lista com todos os seus descendentes. Esta lista é obtida da seguinte forma: concatenei todos os resultados recursivos, de seguida apliquei ao par |lstr|, para obter uma lista com pares "pai" e lista de descendentes, depois para todos os elementos aplico |cons| para que o "pai" seja adicionado a cabeça de todos os elementos. Por fim aplico |cons| para juntar o par. % fazer diagrama do hilo Sendo tax e post, um anaformismo e um catamorfismo de árvores |Exp|, respetivamente, o problema 2 poderia ser reescrito na forma de hilomorfismo, em que o gene do anamorfismo seria o gene de tax e o gene do catamorfismo o gene de post. Obtendo assim a seguinte função. \begin{code} acm_xls_hylo = acm_hylo acm_ccs acm_hylo = hyloExp gene_post gene \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ S^* \ar[d]_-{|acm_hylo|} \ar[r]^-{|gene|} & S + S \times (S^*)^* \ar[d]^-{id + id \times |acm_hylo|^*} \\ (S^*)^* & S + S \times ((S^*)^*)^* \ar[l]^-{|gene_post|} } \end{eqnarray*} % ------------ Problema 3 ----------------- \subsection*{Problema 3} \begin{code} squares = anaRose gsq \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ |Rose Square| & |Square| \times (|Rose Square|)^* \ar[l]_-{|inRose|} \\ |Square| \times |Nat0| \ar[u]^-{squares} \ar[r]_-{gsp} & |Square| \times (|Square| \times |Nat0|)^* \ar[u]_-{id \times |squares|^*} } \end{eqnarray*} \begin{code} gsq = (either (id >< nil) (split p1 surrounding_squares)) . distr . (center_square >< outNat) center_square ((x,y),s) = ((x+(s/3),y+(s/3)),(s/3)) \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ |Square| \times |Nat0| \ar[d]^-{|center_square >< outNat|} \\ |Square|^* \times (1 + |Nat0|) \ar[d]^-{|distr|} \\ (|Square| \times 1) + (|Square| \times |Nat0|) \ar[d]^-{|either (id >< nil) (split p1 surrounding_squares)|} \\ |Square| \times (|Square| \times |Nat0|)^* } \end{eqnarray*} O gene de |squares|, inicialmente calcula o quadrado central e cria uma alternativa para se poder distinguir o caso do número recebido ser 0 ou não. De seguida atrvés de |distr| é o quadrado central é distribuído pelas duas alternativas. Caso o número de input seja 0, é conservado o quadrado e é criada uma lista vazia. Caso contrário calcula-se todos os quadrados circundantes, e mais uma vez conserva-se o quadrado inicial. \begin{code} surrounding_squares = rstr . ((conc . split top_squares (conc . split middle_squares bottom_squares)) >< id) \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ |Square| \times |Nat0| \ar[d]^-{|(conc . split top_squares (conc . split middle_squares bottom_squares)) >< id|} \\ |Square|^* \times |Nat0| \ar[d]^-{|rstr|} \\ (|Square| \times |Nat0|)^* } \end{eqnarray*} A função |surrounding_squares| calcula a lista de todos os quadrados circundantes, e com a utilização de |rstr| anexa a todos os seus elementos o número natural recebido, criando assim uma lista de pares, que vai ser usada nas chamadas recursivas do funtor. \begin{code} top_squares = cons . split sub_side_to_x (cons . split id (singl . add_side_to_x)) . add_side_to_y middle_squares = cons . split sub_side_to_x (singl . add_side_to_x) bottom_squares = cons . split sub_side_to_x (cons . split id (singl . add_side_to_x)) . sub_side_to_y \end{code} Diagrama de |top_squares|: \begin{eqnarray*} \xymatrix@@C=2cm{ |Square| \ar[d]^-{|add_side_to_y|} \\ |Square| \ar[d]^-{|split sub_side_to_x (cons . split id (singl . add_side_to_x))|} \\ |Square| \times |Square|^* \ar[d]^-{|cons|} \\ |Square|^* } \end{eqnarray*} A função |top_squares| calcula os 3 quadrados do superiores da grelha. Para obter a sua definição, primeiro adicionei à coordenada y do quadrado de input, ou seja o quadrado central, o lado do quadrado, deste modo obteremos o quadrado central superior. De seguida, a partir de um split, do lado esquerdo subtraio à coordenada x o seu lado, para obter o quadrado superior esquerdo, e do lado direito, uma lista com o quadrado central e o quadrado superior direito. Finalmente junto o quadrado esquerdo à cabeça, resultando numa lista com todos os quadrados superiores. As funções |middle_squares| e |bottom_squares| seguem a mesma lógica, sendo ligeiramente diferente para os quadrados centrais, onde ignoro o quadrado central. Operações auxiliares sobre os quadrados: \begin{code} add_side_to_x ((x,y),s) = ((x+s,y),s) add_side_to_y ((x,y),s) = ((x,y+s),s) sub_side_to_x ((x,y),s) = ((x-s,y),s) sub_side_to_y ((x,y),s) = ((x,y-s),s) \end{code} Função de conversão de listas em |Rose| Trees: \begin{code} rose2List = cataRose gr2l gr2l = cons . (id >< concat) \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ |Rose A| \ar[r]^-{|outRose|} \ar[d]_-{|rose2List|} & A \times (|Rose A|)^* \ar[d]^-{id \times |rose2List|^*} \\ A^* & A \times (A^*)^* \ar[l]^-{|gr2l|} } \end{eqnarray*} A partir do diagrama do catamorfismo |rose2List|, facilmente se chega à definição do seu gene. Sabendo que o seu funtor origina um par, apenas temos de concatenar os resultado das chamadas recursivas, representado por |(A^*)^*| e adicionar conteúdo do nodo, representado por |A| à cabeça da lista. Componentes da função |constructSierp|: \begin{code} carpets = anaList gcarp gcarp = (id -|- (split (curry sierpinski ((0,0),32)) id)) . outNat \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ (|Square|^*)^* & 1 + |Square|^* \times (|Square|^*)^* \ar[l]_-{|inList|} \\ |Nat0| \ar[u]^-{|carpets|} \ar[r]_-{|gcar|} \ar[d]_-{|outNat|} & 1 + |Square|^* \times |Nat0| \ar[u]_-{id + id \times |carpets|} \\ 1 + |Nat0| \ar[ur]_-{|id| + |(split (curry sierpinski ((0,0),32)) id)|} & } \end{eqnarray*} Para definir o gene |gcar|, primeiro aplico |outNat|. Caso este seja 0, simplesmente devolve o elemento nulo, caso contrário, cria um par com a lista dos quadrados resultantes da função |curry sierpinski ((0,0),32)| para esse número, e o número. \begin{code} present = cataList gprst gprst = either (return . nil) (alpha . (((>> await) . drawSq) >< id)) where alpha (x,y) = do {b <- y ; a <- x ; return (a:b)} \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ (|Square|^*)^* \ar[r]^-{|outList|} \ar[d]_-{|present|} & 1 + |Square|^* \times (|Square|^*)^* \ar[d]^-{id + id \times |present|} \\ |IO(1)|^* & 1 + |Square|^* \times |IO(1)|^* \ar[l]^-{grst} \ar[d]^-{|nil| + |((>>await) . drawSq)| \times id} \\ & 1 + |IO(1)| \times |IO(1)|^* \ar[ul]^-{|either return alpha|} } \end{eqnarray*} \subsection*{Problema 4} \subsubsection*{Versão não probabilística} Gene de |consolidate'|: \begin{code} cgene :: (Eq a, Num b) => Either () ((a,b),[(a,b)]) -> [(a,b)] cgene = either nil add_pair add_pair ((x,y),t) = (x, y + maybe 0 id (List.lookup x t)):(filter(\(a,b) -> a != x) t) \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ (A \times B)^* \ar[r]^-{|outList|} \ar[d]_-{|consolidate'|} & 1 + (A \times B) \times (A \times B)^* \ar[d]^-{id + id \times |consolidate'|} \\ (A \times B)^* & 1 + (A \times B) \times (A \times B)^* \ar[l]^-{|cgene|} } \end{eqnarray*} Após desenhar o diagram do catamorfismo de |consolidate'|, cheguei à seguinte definição do seu gene. Caso este receba o elemento nulo, devolve a lista vazia, caso contrário, com a ajuda da função |add_pair|, procura na lista resultante da chamada recursiva por elementos que tenham a mesma "chave" e, se ela existir adiciona o conteúdo associado ao segundo elemento. Por fim retira todos os elementos que tenham a mesma "chave" e adiciona a cabeça o novo par. Geração dos jogos da fase de grupos: \begin{code} pairup [] = [] pairup (h:t) = curry lstr h t ++ pairup t \end{code} A função |pairup| está encarregue de criar todos as possibilidades de jogos entre equipas. Para isso, recebendo uma lista de equipas, com o auxílio o função |lstr| cria uma lista com todos os pares entre a cabeça e o resto dos elementos, deste modo não existirão pares repetidos. Por fim adiciona-os à cabeça da lista resultante da chamada recursiva, retornando a lista com todos os jogos possíveis não repetidos. \begin{code} matchResult crit m = teamPoints m (crit m) teamPoints (t1,t2) Nothing = [(t1,1),(t2,1)] teamPoints (t1,t2) (Just t) = if t1 == t then [(t1,3),(t2,0)] else [(t2,3),(t1,0)] \end{code} A função |matchResult|, aplica um critério recebido ao jogo também recebido, e de seguida, usando a função |teamPoints| cria a lista com o resultado do jogo. Caso o resultado seja |Nothing|, que significa um empate, ambas as equipas recebem 1 ponto, caso contrário a equipa vencedora recebe 3 pontos. \begin{eqnarray*} \xymatrix@@C=2cm{ |LTree A| & A + |LTree A| \times |LTree A| \ar[l]_-{|inLTree|} \\ A^* \ar[u]^-{|anaLTree glt|} \ar[r]_-{|glt|} & A + A^* \times A^* \ar[u]_-{id + |anaLTree glt| \times |anaLTree glt|} } \end{eqnarray*} \begin{code} glt = (id -|- ((cons >< id) . assocl . (id >< divideList))) . out where divideList l = (take (length l `div` 2) l, drop (length l `div` 2) l) \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ A^* \ar[d]^-{|out|} \\ A + A \times A^* \ar[d]^-{|id| + |id| \times divideList} \\ A + A \times (A^* \times A^*) \ar[d]^-{|id| + |assocl|} \\ A + (A \times A^*) \times A^* \ar[d]^-{|id| + |cons| \times |id|} \\ A + A^* \times A^* } \end{eqnarray*} Para definir o gene de |anaLTree glt|, comecei por desenhar o seu diagrama. De seguida com ajuda do diagrama acima, apliquei o |out| das listas não vazias, e caso o input seja uma lista com um só elemento, é conservado e retornado. Caso contrário, reparto a cauda da lista em 2 partes, associo o a cabeça à primeira lista, e finalmente adiciono a cabeça à mesma. \subsubsection*{Versão probabilística} \begin{code} pinitKnockoutStage = return . initKnockoutStage \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ (|Team|^*)^* \ar[d]^-{|initKnockoutStage|} \\ |LTree Team| \ar[d]^-{|return|} \\ |Dist (LTree Team)| } \end{eqnarray*} Analisando a função |pgroupStage|, reparei que a função |pinitKnockoutStage| está em composição monádica com a função |psimulateGroupStage| logo o seu input já será o conteúdo do mónade. Sendo assim pode-se reaproveitar a função definida para a versão não probabilística, |initKnockoutStage| e de seguida retornar esse resultado, de forma a devolver o mónade |Dist (LTree Team)|. \begin{code} pgroupWinners :: (Match -> Dist (Maybe Team)) -> [Match] -> Dist [Team] pgroupWinners criteria = fmap fmapBody . sequence . map (pmatchResult criteria) fmapBody = best 2 . consolidate . concat pmatchResult criteria match = do {dist <- criteria match ; return (teamPoints match dist)} \end{code} \begin{eqnarray*} \xymatrix@@C=2cm{ |Match|^* \ar[d]^-{|pmatchResult criteria|} \\ (|Dist| (Match \times |Nat0|)^*)^* \ar[d]^-{|sequence|} \\ |Dist| ((|Match| \times |Nat0|)^*)^* \ar[d]^-{|fmap fmapBody|} \\ |Dist Team|^* } \end{eqnarray*} Para a função |pgroupWinners|, começo por aplicar a função |pmatchResult| a todos os elementos da lista de jogos, de forma obter uma lista de mónades de distribuições com a lista de todos os resultados possíveis dos jogos. De seguida, aplicando |sequence| transforma-se a lista de mónades num mónade de listas, em que o seu resultado é a distribuição probabilística de todas as combinações dos resultados dos jogos. Usando |fmap|, altera-se o conteúdo do mónade com a função |fmapBody|. Para definir a função |fmapBody|, inicialmente concatenam-se todos os elementos da lista, neste caso os resultados dos jogos, de seguida |consolidate| de forma a obter os pontos finais das equipas, e com |best 2| retornam-se as 2 equipas com os melhores resultados. Esta função irá retornar uma distribuição probabilística das 2 melhores equipas do grupo. %----------------- Índice remissivo (exige makeindex) -------------------------% \printindex %----------------- Bibliografia (exige bibtex) --------------------------------% \bibliographystyle{plain} \bibliography{cp2223t} %----------------- Fim do documento -------------------------------------------% \end{document}