HaskCalc/cp2223t/cp2223t.lhs

1605 lines
58 KiB
Text
Raw Permalink Normal View History

2022-11-19 11:35:14 +00:00
\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
2022-11-19 11:35:14 +00:00
\\\hline
a93248 & João Tiago Alves Fidalgo de Sousa
2022-11-19 11:35:14 +00:00
\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
2023-01-07 23:58:24 +00:00
import Nat hiding (add_pair)
2022-11-19 11:35:14 +00:00
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]
2023-01-11 23:07:05 +00:00
$ bibtex cp2223t.aux
2022-11-19 11:35:14 +00:00
$ 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
2022-12-13 15:36:10 +00:00
\begin{eqnarray*}
2022-11-19 11:35:14 +00:00
|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><sum) . collect
collect :: (Eq a, Eq b) => [(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
2022-12-13 15:36:10 +00:00
2022-11-19 11:35:14 +00:00
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)
2022-11-19 11:35:14 +00:00
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*}
2022-12-13 15:36:10 +00:00
\begin{code}
gene = (id -|- (id >< (groupBy (\x y -> countSpaces y > 0) . map (drop 4)))) . out where
countSpaces = length . takeWhile (== ' ')
2022-11-19 11:35:14 +00:00
\end{code}
% explicacao do gene
2023-01-13 23:49:30 +00:00
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 ---------------
2022-11-19 11:35:14 +00:00
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)))
2022-11-19 11:35:14 +00:00
\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*}
2023-01-13 23:49:30 +00:00
Desenhando o diagrama do catamorfismo de árvores |Exp|, a partir do seu funtor, descobri o tipo de entrada do gene.
2023-01-15 00:37:35 +00:00
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:
2023-01-13 23:49:30 +00:00
concatenei todos os resultados recursivos, de seguida apliquei ao par |lstr|, para obter uma lista com pares "pai" e lista de descendentes,
2023-01-15 00:37:35 +00:00
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
2023-01-13 23:49:30 +00:00
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}
2022-11-19 11:35:14 +00:00
2023-01-15 00:37:35 +00:00
\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 -----------------
2022-11-19 11:35:14 +00:00
\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*}
2023-01-13 23:49:30 +00:00
\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}
2023-01-15 00:37:35 +00:00
Função de conversão de listas em |Rose| Trees:
\begin{code}
2022-11-19 11:35:14 +00:00
rose2List = cataRose gr2l
gr2l = cons . (id >< concat)
\end{code}
\begin{eqnarray*}
\xymatrix@@C=2cm{
|Rose A|
\ar[r]^-{|outRose|}
2023-01-13 23:49:30 +00:00
\ar[d]_-{|rose2List|}
&
A \times (|Rose A|)^*
\ar[d]^-{id \times |rose2List|^*}
\\
A^*
&
A \times (A^*)^*
2023-01-13 23:49:30 +00:00
\ar[l]^-{|gr2l|}
}
\end{eqnarray*}
2023-01-13 23:49:30 +00:00
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.
2023-01-15 00:37:35 +00:00
Componentes da função |constructSierp|:
\begin{code}
2023-01-25 22:00:31 +00:00
carpets = anaList gcarp
2022-12-16 15:59:10 +00:00
2023-01-13 23:49:30 +00:00
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|}
2023-01-13 23:49:30 +00:00
\ar[d]_-{|outNat|}
&
2023-01-13 23:49:30 +00:00
1 + |Square|^* \times |Nat0|
\ar[u]_-{id + id \times |carpets|}
2023-01-13 23:49:30 +00:00
\\
1 + |Nat0|
\ar[ur]_-{|id| + |(split (curry sierpinski ((0,0),32)) id)|}
&
}
\end{eqnarray*}
2023-01-13 23:49:30 +00:00
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
2023-01-25 22:00:31 +00:00
alpha (x,y) = do {b <- y ; a <- x ; return (a:b)}
2022-11-19 11:35:14 +00:00
\end{code}
\begin{eqnarray*}
\xymatrix@@C=2cm{
(|Square|^*)^*
\ar[r]^-{|outList|}
\ar[d]_-{|present|}
&
2023-01-15 00:37:35 +00:00
1 + |Square|^* \times (|Square|^*)^*
\ar[d]^-{id + id \times |present|}
\\
|IO(1)|^*
&
2023-01-15 00:37:35 +00:00
1 + |Square|^* \times |IO(1)|^*
\ar[l]^-{grst}
2023-01-15 00:37:35 +00:00
\ar[d]^-{|nil| + |((>>await) . drawSq)| \times id}
\\
&
1 + |IO(1)| \times |IO(1)|^*
\ar[ul]^-{|either return alpha|}
}
\end{eqnarray*}
2023-01-13 23:49:30 +00:00
2022-11-19 11:35:14 +00:00
\subsection*{Problema 4}
\subsubsection*{Versão não probabilística}
Gene de |consolidate'|:
\begin{code}
2023-01-07 23:58:24 +00:00
2023-01-11 23:07:05 +00:00
cgene :: (Eq a, Num b) => Either () ((a,b),[(a,b)]) -> [(a,b)]
cgene = either nil add_pair
2023-01-07 23:58:24 +00:00
add_pair ((x,y),t) = (x, y + maybe 0 id (List.lookup x t)):(filter(\(a,b) -> a != x) t)
2023-01-13 23:49:30 +00:00
\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*}
2023-01-15 00:37:35 +00:00
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.
2023-01-07 23:58:24 +00:00
2023-01-11 23:07:05 +00:00
2022-11-19 11:35:14 +00:00
Geração dos jogos da fase de grupos:
2023-01-15 00:37:35 +00:00
2022-11-19 11:35:14 +00:00
\begin{code}
2023-01-13 23:49:30 +00:00
pairup [] = []
2023-01-15 00:37:35 +00:00
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.
2023-01-15 00:37:35 +00:00
\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)]
2023-01-15 00:37:35 +00:00
\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.
2023-01-07 23:58:24 +00:00
2023-01-15 00:37:35 +00:00
\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}
2023-01-07 23:58:24 +00:00
glt = (id -|- ((cons >< id) . assocl . (id >< divideList))) . out where
divideList l = (take (length l `div` 2) l, drop (length l `div` 2) l)
2023-01-13 23:49:30 +00:00
\end{code}
2023-01-15 00:37:35 +00:00
\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.
2023-01-13 23:49:30 +00:00
2023-01-15 00:37:35 +00:00
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.
2022-11-19 11:35:14 +00:00
\subsubsection*{Versão probabilística}
\begin{code}
2023-01-13 23:49:30 +00:00
pinitKnockoutStage = return . initKnockoutStage
\end{code}
2023-01-15 00:37:35 +00:00
\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)|.
2022-11-19 11:35:14 +00:00
2023-01-13 23:49:30 +00:00
\begin{code}
2022-11-19 11:35:14 +00:00
pgroupWinners :: (Match -> Dist (Maybe Team)) -> [Match] -> Dist [Team]
2023-01-15 00:37:35 +00:00
pgroupWinners criteria = fmap fmapBody . sequence . map (pmatchResult criteria)
fmapBody = best 2 . consolidate . concat
2023-01-11 23:07:05 +00:00
pmatchResult criteria match = do {dist <- criteria match ; return (teamPoints match dist)}
2022-11-19 11:35:14 +00:00
\end{code}
2023-01-15 00:37:35 +00:00
\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.
2022-11-19 11:35:14 +00:00
%----------------- Índice remissivo (exige makeindex) -------------------------%
\printindex
%----------------- Bibliografia (exige bibtex) --------------------------------%
\bibliographystyle{plain}
\bibliography{cp2223t}
%----------------- Fim do documento -------------------------------------------%
\end{document}