Td 3
Sommaire de documents
Cet exercice propose de manipuler et d’afficher une représentation de sommaire qui structure les différentes parties d’un document. Cette représentation est implémentée par les types suivants :
data Section = Section String [Section]
type Sommaire = [Section]type ou data ?
Il y a plusieurs différences entre data et type en Haskell.
-
datadéfinit un nouveau type distinct, avec ses propres constructeurs. Ces types supportent du filtrage de motif et peuvent définir des types plus complexes. -
typedéfinit un synonyme de type, un alias pour un type existant. Il n’y a pas de constructeur et cela permet essentiellement de rendre du code existant plus clair.
On utilise data quand :
- on doit définir un type avec ses propres constructeurs ;
- on a besoin d’utiliser du filtrage de motif ;
- on veut un type qui peut représenter plusieurs possibilités (
Either,Maybe…).
On utilise type quand :
- on veut simplifier des déclarations de type ;
- on veut donner des noms concrets à des types existants.
Le type Section t som représente une section avec un titre t et le
sommaire de ses sous-parties som. Le type Sommaire est donc
simplement une liste de Section.
Étant donné un sommaire de la forme :
lyah :: Sommaire
lyah = [Section "Introduction" [
Section "About this tutorial" [],
Section "So what's Haskell?" [],
Section "What you need to dive in" []],
Section "Starting Out" [
Section "Ready, set, go!" [],
Section "Baby's first functions" [],
Section "An intro to lists" [],
Section "Texas ranges" [],
Section "I'm a list comprehension" [],
Section "Tuples" []]
]On veut pouvoir afficher une chaîne de caractères qui affiche le sommaire de façon numérotée comme ceci :
1 Introduction
..1.1 About this tutorial
..1.2 So what's Haskell?
..1.3 What you need to dive in
2 Starting Out
..2.1 Ready, set, go!
..2.2 Baby's first functions
..2.3 An intro to lists
..2.4 Texas ranges
..2.5 I'm a list comprehension
..2.6 Tuples1. Transformer sections et sommaires en chaînes de caractères
On implémente deux fonctions (mutuellement récursives) :
-
sommaireToStrings :: Sommaire -> [String], qui transforme un sommaire en la liste des lignes utilisées pour son affichage ; -
sectionToStrings :: Section -> [String]qui transforme une section en la liste des lignes utilisées pour son affichage.
sommaireToStrings :: Sommaire -> [String]
sommaireToStrings = concatMap sectionToStrings
sectionToStrings :: Section -> [String]
sectionToStrings (Section t som) = t : sommaireToStrings somLe principe, c’est que sommaireToStrings va appliquer
sectionToStrings à chaque élément de son sommaire, et concaténer les
résultats (sinon, on se retrouve avec des listes de listes de listes).
2. Numérotation des sections et sommaires
Tout ceci, c’est très bien, mais on n’a pas de numérotation des éléments.
On va donc procéder de la même manière que précédemment, mais en ajoutant la numérotation. On aura deux fonctions :
-
numSommaire :: String -> Sommaire -> Sommaire, qui va renvoyer un nouveau sommaire dont tous les titres de sections sont modifiés pour faire figurer devant une chaîne de caractères et le numéro ; -
numSection :: String -> Int -> Section -> Section, qui va renvoyer une nouvelle section avec un titre obtenu en ajoutant une chaîne et un numéro, et un sommaire précédé de".."pour les décaler dans l’affichage.
numSommaire :: String -> Sommaire -> Sommaire
numSommaire s som = zipWith (numSection s) [1..] som
-- numSommaire s = zipWith (numSection s) [1..] en eta-reduite
numSection :: String -> Int -> Section -> Section
numSection s k (Section t som) = let
t' = s ++ show k ++ " " ++ t
s' = ".." ++ s ++ show k ++ "."
som' = numSommaire s' som
in (Section t' som')Quand on est face à un sommaire, on utilise la liste [1..] en prime
pour avoir la numérotation et on zippe avec la fonction numSection,
qui va numéroter chaque section.
La numérotation d’une section est le numéro devant le titre et ensuite
on rappelle la numérotation sur le sommaire de la section, avec une
chaîne de départ décalée et pré-numérotée.
Si on reprend notre exemple de tout à l’heure, on a :
numSommaire "" lyah --> zipWith (numSection "") [1..] lyah
--> numSection "" 1 Section "Introduction" [Section "About this tutorial" [], ...]
--> t' = "1 Introduction"
s' = "..1."
som' = numSommaire "..1." [Section "About this tutorial" [], ...]
--> zipWith (numSection "..1.") [1..] [Section "About this tutorial" [], ...]
--> t' = "..1.1 About this tutorial"
après la liste est vide
<-- Section "1 Introduction" [Section "..1. About this tutorial" [] ...]
etc3. Afficher le sommaire
On va écrire une fonction showSommaire :: Sommaire -> String qui
numérote un sommaire puis le transforme en chaîne de caractères.
On va utiliser la fonction unlines :: [String] -> String qui
concatène une liste de chaînes avec des sauts de lignes.
showSommaire :: Sommaire -> String
showSommaire = unlines . sommaireToStrings . numSommaire ""Si on affiche le résultat sur notre exemple, on a bien :
ghci> putStrLn (showSommaire lyah)
1 Introduction
..1.1 About this tutorial
..1.2 So what's Haskell?
..1.3 What you need to dive in
2 Starting Out
..2.1 Ready, set, go!
..2.2 Baby's first functions
..2.3 An intro to lists
..2.4 Texas ranges
..2.5 I'm a list comprehension
..2.6 Tuples