Son nom vient du mathématicien Haskell Brooks Curry. Haskell est un langage fonctionnel, c'est-à-dire que tout est fait avec des appels de fonction. Il est typé statiquement et implicitement, les types sont vérifiés par le compilateur et il n'est pas nécessaire de les déclarer. Il est paresseux, rien n'est fait avant que ce ne soit nécessaire.
GHC (Glasgow Haskell Compiler) est un compilateur libre pour le langage Haskell développé par l'Université de Glasgow sous licence BSD. Pour le télécharger, aller sur le site http://www.haskell.org/
Il accompagne le compilateur GHC, et permet d'exécuter à la volée des instructions haskell. C'est un interpréteur. Il permet également de lancer des commandes systèmes, de chargement de fichiers, d'édition, d'information, de contrôle, de débugage... Il peut charger des modules compilés ou interprétés.
Pour le lancer dans une fenêtre MSDOS, exécuter GHCi.
WinGHCi est la version graphique de GHCi.
On peut Spécifier l'éditeur que l'on souhaite utiliser (ici l'éditeur Notepad++) en lanceant la commande suivante :
File|Option Editor : C:\Program Files\SciTE\Notepad++.exe
ou la ligne de commande suivante :
:set editor C:\Program Files\SciTE\Notepad++.exe
Hugs est un interpréteur bytecode. Il offre une compilation rapide du Haskell en bytecode et une vitesse d'exécution raisonnable. Il possède aussi une bibliothèque graphique simple. Il permet de lancer des commandes système comme dans l'interpreteur GHCi. Hugs se prête bien à l'apprentissage de Haskell, mais il ne faut pas en déduire que Hugs est une implémentation simpliste. C'est la plus portable et la plus légère des implémentations de Haskell. Pour télécharger l'interpréteur Hugs, aller sur le site http://cvs.haskell.org/Hugs/.
Pour lancer l'interpreteur Hugs dans une fenêtre MSDOS, exécuter la commande "C:\Program Files\WinHugs\hugs.exe".
WinHugs est la version graphique de Hugs.
Notpad++ est un éditeur de code source qui prend en charge plusieurs langages dont haskell. Il donne une coloration syntaxique pour de nombreux langages. Pour le télécharger, aller sur le site http://notepad-plus-plus.org/fr/
SciTE est un éditeur de code source qui prend en charge plusieurs langages. Il donne une coloration syntaxique pour de nombreux langages, et on peut facilement en ajouter d'autres. Pour le télécharger, aller sur le site http://www.scintilla.org/
La partie droite de l'éditeur se comporte comme une fenêtre de commande MSDOS
Pour ajouter le langage Haskell à sa liste de langage, faite comme suit :
1- Copier le fichier haskell.properties dans le répertoire C:\Program Files\SciTE\
2- Modifier le fichier SciTEGlobal.propertie (ouvrable dans SciTE via Option|Open Global Options File) comme suit :
Ajoutez-y l'extention hs dans la commande suivante :
source.files=*.asm;*.c;*.cc;*.cpp;*.cxx;*.cs;*.h;*.hs;*.hh;*.hxx;*.hp
Ajoutez-y le langage Haskell dans la commande "
menu.language=
" entre le langage Gap et Hypertext comme suit :
...
Ajoutez-y le langage Haskell dans la liste des imports entre le langage Gap et Hypertext comme suit :
#Gap|g||\
&Haskell|hs||\
H&ypertext|html|F12|\
...
...
#import gap
import haskell
import html
...
Créez le fichier hello.hs et placez-y le programme Haskell suivant :
main = putStrLn "Bonjour"
Compilez-le avec la commande
ghc -o hello hello.hs
Cela crée dans le même répertoire le fichier : hello.o, hello.hi, et le fichier exécutable hello.exe
|
|
http://onlamp.com/pub/a/onlamp/2007/05/21/an-introduction-to-haskell---part-1-why-haskell.html
Adam Turoff - 2007
Traduction
http://progmod.org/tutoriel/2/programmez-en-haskell/ Site du Zéro
gnomnain
http://litpc9.ulb.ac.be/lang/index.php/Haskell
Stefan Langerman, Frédéric Pluquet
Haskell 98 Language and Libraries, The Revised Report
www.haskell.org
- 2002
Real World Haskell
Bryan O'Sullivan, Don Stewart, and John Goerzen - 2007-2008
http://en.wikibooks.org/wiki/Haskell
en.Wikibooks.org
http://cs.fit.edu/~ryan/cse4250/haskell.html
Stansifer, Ryan.
Quel but recherchons nous dans une tel documentation ? le but est, dans le cadre de la démarche d'appréhendement du langage Haskell (autrement dit dans le cadre de son apprentissage) par des programmeurs déja au faite des méthodes de programmation impérative, object, fonctionnelle et logique, de trouver rapidement et avec le moins d'effort possible, une instruction haskell résolvant une étape de progrrammation nécessaire à la résolution d'un problèmes plus vaste. (voir §10.3 La documentation et le langage ).
L'expérience nous montre combien la synoptique par l'exemple est plus pratique que celle par définition, et que les conventions de nommage propre à la documentation facilitent son utilisation sans imposer de containte supplémentaire au language.
L'ordre dans lequel est présenté le langage, comme un jeu de légos, présentant en premier les pièces les plus simples et les plus génériques, pour montrer en second les différentes constructions que l'on peut faire avec, donne une assurance à celui qui apprend, ainsi que la capacité de créer par lui-même et donc de s'automotiver.
3.1) Valeurs et Types
Dans Haskell, les valeurs correspondent à des éléments, et les types correspondent à des ensembles d'éléments. Il existe des variables contenant des valeurs et des variables de type contenant des types. Ces variables sont logique, c'est à dire qu'elle ne subissent pas d'affectation autre que leur déclaration initiale mais parcontre sont soumisent à des unifications, et sont relatives à une partie du code tel les paramètres d'entrés d'une fonction appellés communément variables muettes.
Les valeurs peuvent être passés à des fonctions en tant qu'arguments, retournées en tant que résultats, placées dans des listes ou des tuples ou dans la tête et le corps d'une fonction, par contre il n'en est pas de même pour les types.
Les identifants se compose de minuscules, majuscules, chiffres, du caractère souligné _ et du caractère prime '.
Les variables contiennent des valeurs, et leur nom commencent par une minuscule (ou par le caractère '_'). Et dans la documentation les variables sont notées de couleur bleu.
Les types nominaux commencent par une majuscule. Dans la documentation les types sont notés en gras.
Les variables de type contiennent des types, et leur nom commence par une minuscule (ou par le caractère '_'). Et dans la documentation les variables de type sont notés en rouge.
3.2) Les types simples
On considère différents types simples et pour chacun de ces types, on se donne des variables génériques possèdant implicitement ce type. Cela évite de redéclarer le type des variables au cours de la documentation et de se perdre dans des détails qui sont posés une fois pour toutes.
Type simple |
Description |
Exemple |
Variables génériques pour la documentation |
Char |
Les caractères | 'a' |
c |
Int |
Les entiers signés sur 32 bits | 120 |
i
|
Integer |
Les entiers signés | 120 |
n , m |
Double |
Les nombres à virgule | 3.14 |
x , y |
Bool |
Les valeurs booléennes | True |
b
|
3.3) Les trois procédés de construction d'éléments : liste, tuple, fonction
On considère les trois procédés de construction d'éléments que sont les listes, les tuples et les fontions.
[1,2,3]
, []
, [True,False,True]
, [[],[1],[1,2]]
(5, 'a', True)
, ()
, (1, 2, 3)
, ((),[])
. Noter qu'un tuple de taille 1 n'existe pas car (e) = e
\x->x+1
, \(x,y)->x*y
, \[x]->x
, \(x:xs,y)->y:xs
Lorsque nous étendons le langage d'élément pour construire de nouveaux éléments que sont les listes, les tuples et les fonctions, nous étendons également le langage des types permettant de construire de nouveaux types que sont les différents types de listes, les différents types de tuples et les différents types de fonctions.
3.4) Les types de liste
Le type de liste le plus générale est noté [a] où a joue le rôle d'une variable de type sur laquelle il n'y a ici aucune condition. Puis pour chaque types on peut définir le type de liste d'éléments de ce type comme par exemple :
Type |
Description |
Exemples d'éléments |
[ a ] |
Type des listes | [True,True] , [[],[5]] , "Bonjour" , [1,2,3] |
[Integer] |
Type des listes d'entiers | [1,2,3] |
[Char] |
Type des listes de caractères = Type String | "Bonjour" |
[Bool] |
Type des listes de booléens | [True,True,False] |
[[ a ]] |
Type des listes de listes | [[],[5]] , ["Hello","By"] |
[[Bool]] |
Type des listes de listes de booléens | [[True,True], [False],[]] |
[( a , b )] |
Type des listes des couples | [(5,'a'),(2,'b')] , [(True,[1]), (False,[2])] , [(1,2)] |
3.5) Les types de tuple
Un couple est un tuple de taille 2, et un triplet est un tuple de taille 3. Le type de couple le plus générale est noté (a,b) où a et b jouent le rôle de variables de type sur lesquelles il n'y a ici aucune restriction. Puis on peut restreindre ces variables de type a et b en les unifiants à d'autre types :
Type |
Description |
Exemples d'éléments |
(a,b) |
Type des couples | (5,'a') , ([2],(5,'a')) , ((),[]) , (True, "Hello") |
(Integer,a) |
Type des couples d'un entiers et d'un éléments | (5,'a') , (8,False) , (1,2) , (2,"Bonjour") , (3,[1,2]) |
(Integer, Bool) |
Type des couples d'un entiers et d'un booléen | (8, False) , (0, True) , (1,True) |
([a],a) |
Type des couples d'une liste et d'un élément de même type que ceux de la liste | ([1,2],3) , ("Bonjour",'a') , ([True], False) , ([],1) |
3.6) Les types de fonction
Une fonction f possède un type a d'éléments de départ et un type b d'éléments d'arrivé, et on dit alors que f possède le type a->b. Une fonction la plus générale est de type a->b où a et b jouent le rôle de variables de type sur lesquelles il n'y a ici aucune condition. a représente l'ensemble de départ et b l'ensemble d'arrivé.
Type |
Description |
Exemple d'élément |
a->b. |
Type des fonctions | \x->[x,x*x] |
Int->Int |
Type des fonctions de Int vers Int | \x->x+1 |
Char->Bool |
Type des fonction de Char vers Bool | \c->c=='a' |
(Int,Int)->Int |
Type des fonction de (Int,Int) vers Int | \(x,y)->x*y |
[Int,Int]->Int |
Type des fonction de [Int,Int] vers Int | \[x,y]->x*y |
Int ->(Int->Int) |
Type des fonction de Int vers Int->Int | \x->(\y->x*y) |
(Int->Int)->Int |
Type des fonction de Int->Int vers Int | \f->f x |
Par défaut le module Prélude ne prévoit pas d'afficher les éléments qui sont des fonctions, et donc dans l'interpréteur, lorsque on retourne une valeur qui est une fonction, celui-ci génère une erreur : No instance for (Show ...). Cela étant, on pourrait tout à fait concevoir une méthode d'affichage. Les variables, les listes et les tuples peuvent contenir des fonctions, mais alors elles ne peuvent pas s'afficher dans l'interprèteur.
3.7) Les variables génériques pour la documentation
On complète la liste des variables génériques pour la documentation :
Type simple |
Description |
Exemple |
Variables génériques pour la documentation |
[a] |
Les listes |
[1,2,3] , "abc" |
l , s |
(a,b) |
Les couples |
(5, True) |
u |
a->b |
Les fonctions |
\x->x*x |
f , g , h |
a->Bool |
Les fonctions caractristiques |
\x->even x |
p , q |
3.8) Les opérateurs
Opérateur Appellation Appel Sense (()) Priorité Applicationf x
gauche 10!!
Indexagel
!!n
gauche 9.
Compositionf
.g
droite 9^
Exponentiationx
^n
droite 8**
Exponentiationx
^y
droite 8*
Mulptiplicationx
*y
gauche 7/
Divisionx
/y
gauche 7`div`
Division entièren
`div`m
gauche 7`mod`
Modulon
`mod`m
gauche 7+
Plusx
+y
gauche 6-
Moinsx
-y
gauche 6:
Construction de listex
:l
droite 5++
Concaténationl1
++l2
droite 5/=
Différentx
/=y
aucun 4==
Egalex
==y
aucun 4<
Inférieurx
<y
aucun 4<=
Inférieur ou égalex
<=y
aucun 4>
Supérieurx
>y
aucun 4>=
Supérieur ou égalex
>=y
aucun 4`elem`
Appartientx
`elem`l
aucun 4`notElem`
N'appartient pasx
`notElem`l
aucun 4&&
Etb1
&&b2
droite 3||
Oub1
||b2
droite 3L'application s'évalue par la gauche. Cela signifie que : x y z t = ((x y) z ) t
La composition s'évalue par la droite. Cela signifie que : x.y.z.t = x (y (z t))Chaque opérateur infixe, tel que 5 * 6 admettent une notation préfixe (*) 5 6
Chaque opérateur préfixe, tel que f 5 6 admettent une notation infixe 5 `f` 6
3.9) Opérations de base
Commande |
Appellation |
Appel |
Description |
Exemples |
Résultat |
head |
Tête |
head s |
Premier élément de la liste s | head[1,2,3] |
=> 1 |
tail |
Queue |
tail s |
Queue de la liste s | tail[1,2,3] |
=> [2,3] |
last |
Dernier |
last s |
Dernier élément de la liste s | last[1,2,3] |
=> 3 |
: |
Paire pointée |
e : s |
Construction de liste en ajoutant l'élément e à la liste s | 1:[2,3] |
=> [1,2,3] |
length |
Longueur |
length s |
Longueur de la liste s | length[5,2,9] |
=> 3 |
++ |
Concaténation |
s1 ++ s2 |
Concaténation des listes s1 et s2 | [1,2]++[3,4] |
=> [1,2,3,4] |
elem |
Appartenance |
elem e s |
Si e appartient à la liste s | elem 6 [5,6,5] |
|
[ .. ] |
Enumération |
[x..y] |
Liste des éléments consécutifs de x à y | [2..5] |
=> [2,3,4,5] |
take |
Premiers |
take n s |
Liste des n premiers éléments de la liste s | take 2 [0,1,2,3] |
=> [0,1] |
!! |
N-ième |
s !! n |
n-ième élément de la liste s en partant de 0 | [5,6,7,8]!!2 |
=> 7 |
sum |
Somme |
sum s |
Somme des éléments de la liste s | sum[2,10,2] |
=> 14 |
product |
Produit |
product s |
Produit des éléments de la liste s | product[2,3,3] |
=> 18 |
reverse |
Reverse |
reverse s |
Liste s de sense inversé | reverse[5,6,7] |
=> [7,6,5] |
Commande |
Appellation |
Appel |
Description |
Exemples |
Résultat |
fst |
Premier |
fst u |
Premier élément du couple u | fst(1,2) |
=> 1 |
snd |
Second |
snd u |
Premier élément du couple u | snd(1,2) |
=> 2 |
zip |
Zip |
zip s1 s2 |
Liste des couples de même rang | zip[1,2] [5,6] |
=> [(1,5),(2,6)] |
unzip |
Unzip |
unzip s |
Couple de liste à partir d'une liste des couples | unzip[(1,5),(2,6)] |
=> ([1,2],[5,6]) |
zipWith |
ZipWith |
zipWith(op) s1 s2 |
Liste des opérations de même rang | zipWith (+)[(1,5),(2,6)] |
=> (1+2,5+6) |
Exemple de produit et filtrage
[ x|x<-l,f x] |
[ x|x<-[1..5],even x] |
=> [2,4] |
[ (x,y)|x<-l1,y<-l2] |
[ ( x,y)|x<-[1,2],y<-[1,2,3]] |
=> [(1,5),(2,6)] |
... n'est évalué que ce qu'il faut ainsi : take 5 [x | x <- [1..], odd x] => [1,3,5,7,9]
[(x, y) | x <- [1,2], y <- [1,2,3]] => [(1,1),(1,2),(1,3),(2,1),(2,2),(2,3)]
Commande |
Appellation |
Appel |
Description |
Exemples |
Résultat |
takeWhile |
Filtre d'enmagasinage |
takeWhile p s |
Liste les premiers éléments succécifs de s tant qu'il satisfont p | takeWhile odd [1,3,2,5] |
=> [1,3] |
dropWhile |
Filtre de retrait |
dropWhile p s |
Liste les premiers éléments succécifsde s tant qu'il satisfont non p | takeWhile odd [2,4,1,6] |
=> [2,4] |
map |
map |
map f s |
Application de f au éléments de la liste s | map (\x->x+1) [1,2,3] |
=> [2,3,4] |
foldl |
fold left |
foldl f e s |
Itère l'opérateur binaires f par la gauche sur la liste s avec un élément initial e | foldl (*) 5 [1,2,3,4] |
=> 120 -- =(((5*1)*2)*3)*4 |
foldr |
fold rigth |
foldr f e s |
Itère l'opérateur binaires f par la droite sur la liste s avec un élément initial e | foldr (*) 5 [1,2,3,4] |
=> 120 -- =1*(2*(3*(4*5))) |
until |
Jusqu'à ce que |
until p f x |
calule f(f(..f(x)..) jusqu'à ce que le résultat satisfasse p. | until (>100) (*2) 5 |
=> 160 |
Commande |
Appellation |
Appel |
Description |
Exemples |
Résultat |
and |
ET |
and s |
Effectue un ET logique sur les éléments d'une liste s | and [True,False,True] |
=> False |
or |
OU |
or s |
Effectue un OU logique sur les éléments d'une liste s | or [True,False,True] |
=> True |
all |
Tous |
all p s |
Teste si tous les éléments de s satisfont p | all odd [1,3,7] |
=> True |
any |
Au moin un |
any p s |
Teste si il existe un élément de s satisfaisant p | any odd [2,2,3,4] |
=> True |
Commande |
Appellation |
Appel |
Description |
Exemples |
Résultat |
iterate |
iterate f e |
[e, f(e), f(f(e)), f(f(fe)))...] |
take 8 (iterate (*2) 1) |
=> [1,2,4,8,16,32,64,128] |
|
[..] |
[x..] |
[x, x+1, x+2, x+3...] |
take 3 [1..] |
=> [1,2,3] |
|
[x..y] |
x est les succésseurs jusqu'à y | [4..6] |
=> [4,5,6] |
||
[x,y..] |
[x, y, x+2*(y-x), x+3*(y-x)...] |
take 4 [0,2..] |
=> [0,2,4,6] |
||
[x,y..z] |
x est les suivants de pas (y-x) jusqu'à z | [9,6..0] |
=> [9,6,3,0] |
r = 0:r
r!!36 => 0
3+5
2**64
2^^64
2^64
11/3
div 104 5
mod 104 5
divMod 104 5
sqrt 2
pi
exp 3
0xff
0o77
'a'
"Bonjour"
reverse "Bonjour"
=> 8
=> 1.8446744073709552e19
=> 1.8446744073709552e19
=> 18446744073709551616
=> 3.6666666666666665
=> 20
=> 4
=> (20,4)
=> 1.4142135623730951
=> 3.141592653589793
=> 20.085536923187668
=> 255
=> 63
=> 'a'
=> "Bonjour"
=> "ruojnoB"
L'opérateur ::
sert à spécifier le type, et à le vérifier donc au moment de la compilation. Ainsi x :: Integer
retourne x
si x
peut bien être restreint au type Integer
sinon cela déclenche une erreur lors de la compilation. Voici quelques types :
Integer : type des entiers
Int : type des entiers signés sur 32 bits.
Char : type des charactères
[Integer] : type des listes d'entiers
(Char,Integer) : type des couples d'un charactère et d'un entier.
5 :: Integer
'a' :: Char
[2,5,4] :: [Integer]
('b',5) :: (Char,Integer)
[] :: [Integer]
"Bonjour" :: [Char]
=> 5
=> 'a'
=> [2,5,4]
=> ('b',5)
=> []
=> "Bonjour"
succ : la fonction successeur
succ 2
succ(2)
succ(succ 2)
=> 3
=> 3
=> 4
(succ :: Integer->Integer) 3
(succ :: Integer->Integer)(3::Integer)
(succ :: Int->Int)(3::Int)
=> 4
=> 4
=> 4
(succ :: Int->Int)((succ :: Int->Int)(3)
=> 5
3.9) Définition de fonction
Définition directe |
f n = if n==0 then 1 else n*f(n-1) |
Définition avec équations |
f 0 = 1 |
Définition avec fonction de cas |
f n = case n of |
Définition avec gardes |
f n | n==0 = 1 |
f :: (a,b) -> c
(curry f) :: a->b->c
g :: a->b->c
(uncurry g) ::
(a,b) -> c
head et tail peuvent être redéfinis par :
head (x:_) = x
tail (_:s) = s
map peut être redéfini par :
map _ [] = []
map f (x:q) = f x : map f q
let sum = foldl (+) 0; aplat = foldl (++) []
sum [1,2,3,4,5,6] => 21
aplat [[1,2],[5,6],[9]] => [1,2,5,6,9]
until peut être redéfini par :
until p f x = if p x then x else until p f (f x)
map peut être redéfini par :
map _ [] = [] et map f (x:q) = f x : map f q
all et any peuvent être redéfinie par :
all p s = and (map p s)
any p s = or (map p s)
Les fonctions partielles
(*2) 5 => 10
(2*) 5 => 10
(>0) 2 => True
(1/) 2 => 0.5
(/2) 8 => 4
(+1) 2 => 3
3.9) Quelques algorithmes courants
Signe de x | signe x = if x>0 then 1 |
signe x |
Factoriel de x | f n = if n==0 then 1 |
f 0 = 1 |
Résolution de f(x)=0 par dichotomie : On définie la fonction dicho qui prend en argument la fonction f les valeurs a et b de l'intervale de départ et l'erreur (la taille de l'intervale finale) |
|
dicho f a b e |
Les triplets pythagoriciens x^2 + y^2 = z^2 : | tp k=[(x,y,z)|let n=[1..k],x<-n,y<-n,z<-n,x*x+y*y==z*z] |
|
Retrait de la première occurrence de x dans une liste | ret x [] = [] |
ret x [] = [] |
Différence (retrait d'une seul occurence) | diff s [] = s |
diff s [] = s |
Tri par segmentation : O(n*ln(n)) On prend une valeur et on sépare la liste en deux, les valeurs inférieurs et les valeus supérieurs. Puis une foie ces deux listes triées on les concatenne. |
|
|
Tri par insertion : O(n^2) On trie la liste moins le premier élément, et on place l'élément à la bonne place dans la liste triée. |
triins[] = [] |
|
Tri par fusion : O(n*ln(n)) |
trifus[]=[] |
trifus[]=[] |
Les parties d'un ensemble : Les partie de la liste ne contenant pas le premier élément e aux quelles on ajoute les parties de la liste restante aux quelles on ajoute l'élément e. |
parties[]=[[]] |
|
Les permutations d'une listes d'éléments distincts On prend successivement un élémént x suivi de toutes les permutations possibles des autres. |
perm[]=[[]] |
perm[]=[[]] |
Les nombres premiers |
nPremiers = crible([2..]) |
nPremiers = crible([2..]) |
Les nombres premiers cc(s) retourne la liste s en enlevant tous les multiples du premier élément de la liste s. |
nPremiers = map head (iterate cc([2..]) |
|
Liste des diviseurs propres | facteurs n = [x|x<-[1..n-1], mod n x == 0] |
|
Liste des diviseurs propres plus petit ou égale à la racine carré. | facts n = |
|
Les nombres parfaits |
nParfait = filter (\n->sum(facteurs n) == n) [1..] |
|
Suite des factoriels | sf = 1:zipWith (*) sf [1..] |
|
Suite de Fibonacci | sfb =1:1:[u+v|(u,v)<-zip sfb (tail sfb)] |
|
Suite de Fibonacci | sfb =1:1:zipWith (+) sfb (tail sfb) |
|
Triangle de Pascal | pascal = iterate(\s->zipWith(+)([0]++s)(s++[0])) [1] |
|
L'évaluation en profondeur d'abord (innermost) signifie que pour chaque f(x) l'argument x doit être évalué avant la fonction f sur son argument x. Et lorsque toutes les opérations s'évaluent de gauche à droite, cela autorise alors la notation polonaise ou postfixées qui permet comme en Forth de commencer l'évaluation d'une expression au fur et à mesure de sa lecture. L'inconvénient de cette méthode est que l'évaluation de tous les arguments est toujours faites en premiers même si cela n'est pas utile pour le calcul final.
Par contre l'évaluation paresseuse, l'évaluation par l'exterieur d'abord (outermost) signifie que pour chaque f(x) on tente d'évaluer f sur son argument x d'abord, et on évalue x que si c'est necessaire.
f(x)=f(x+1)
g(x)=3
a=1/0
g(f 4) =>
3
g a =>3
a => Infinity
f 2 => ne s'arrète jamais...
Exemple de fonction récurcive primitive | f(n,0) = n+1 |
|
La fonction d'ackermann |
ak(x,y) = if x==0 then y+1 else ak (x-1,if y==0 then 1 else ak (x,y-1)) |
ak (x,y) |
Le backtracking Chercher par balayage en profondeur d'abord, le chemin qui arrive à un état satisfaisant p, à partir d'un état initiale e0, par le moyen d'une fonction s donnant pour chaque états la liste des états suivants possibles. La fonction ariane retourne le couple formé d'un booléen (True si solution trouvé, ou False), et de la liste inversée complètes des états permettant de passer de e0 au premier état satisfaisant p rencontré. |
ariane s p e0 = f [e0][] where |
ariane s p e0 = f [e0] [] where |
Les types définie par constructeurs
Les commandes de plateforme :
|
|
|
:load fichier |
:l fichier |
Charge le fichier fichier.hs le prompt devient Fichier> |
:load |
:l |
Enlève toutes les définitions chargées à l'exception du "Prelude" |
:browse module |
|
Donne la liste des fonctions et leur type dans le module module |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 exercices...
:load nom.hs charge le fichier spécifié. En chargeant un fichier "essai.hs" le prompt devient essai>
:load enlève toutes les définitions chargées à l'exception du "prelude"
:also autre.hs charge un autre fichier
:reload répète la dernière commande de chargemant
:edit nom.hs Ouvre le fichier nom.hs
:edit edite le dernier module
:module <module> pose un module for evaluating expressions
:type <expr> affiche le type d'une expression
:? affiche cette liste de commandes
:set <options> set command line options
:set help on command line options
:names [pat] édite tous les noms contenus
:browse <modules> donne la liste des fonctions et leur type dans le module indiqué
:gc force le garbage collecteur
:quit exit Hugs interpreter
Une expression s'évalue en une valeur et possède un type static.
Il y a 6 sortes d'identificateurs :
- varid (Variables) : commence par une minuscule ou _
- conid (Constructeurs) : commence par une majuscule
- tyvar (Variable de type) : commence par une minuscule ou _
- tycon (Constructeurs de type) : commence par une majuscule
- tycls (Classes de type) : commence par une majuscule
- modid (Modules) : commence par une majuscule
Type de constructeur de nom différent que type classe. Les noms peuvent être qualifié sauf les typvar et les modid.
3.2 Variables, Constructors, Operators, and Literals
aexp -> qvar (variable)
| gcon (general constructor)
| literal
gcon -> ()
| []
| (,{,})
| qcon
var -> varid | ( varsym ) (variable)
qvar -> qvarid | ( qvarsym ) (qualified variable)
con -> conid | ( consym ) (constructor)
qcon -> qconid | ( gconsym ) (qualified constructor)
varop -> varsym | `varid ` (variable operator)
qvarop -> qvarsym | `qvarid ` (qualified variable operator)
conop -> consym | `conid ` (constructor operator)
qconop -> gconsym | `qconid ` (qualified constructor operator)
op -> varop | conop (operator)
qop -> qvarop | qconop (qualified operator)
gconsym -> : | qconsym
ghc -o prog --make Main
ghc -o executable --make ModuleMain
Commande Re Appel exemple Description:help
:?:h
:?:help
Affiche la liste des commandes :cd
:c
:cd
C:\dir1
Change le répertoire courant à C:\dir1
:load:l
:load
:loadToto
:load
*Toto
Charge le module Prélude
Charge le module compilé contenu dans le fichier Toto.hs
Charge le module non-compilé contenu dans le fichier Toto.hs :add :a:add
Toto
:add *TotoCharge le module compilé contenu dans le fichier Toto.o et conserve les références des autres modules. :module
:m
:m+*Toto
:m
+Toto
:m
-Toto
:m
-*Toto
Spécifit le contexte pour l'évaluation des expressions :
Active le module non-compilé Toto
Active le module compilé Toto
Désactive le module compilé Toto
Désactive le module non-compilé Toto:browse
:bro
:load
Toto
:info
:i
:load
Toto
:type :t:load
Toto
:!
:!
:!
dir
:main
:ma
:load
Toto
Commande Re Appel exemple Description:!
:h
:?:! ghc -o Toto Toto.hs
Affiche la liste des commandes :cd
:c
:cd
C:\dir1
Change le répertoire courant à C:\dir1
:load:l
:load
:loadToto
:load
*Toto
Charge le module Prélude
Charge le module compilé contenu dans le fichier Toto.hs
Charge le module non-compilé contenu dans le fichier Toto.hs :add :a:add
Toto
:add *TotoCharge le module compilé contenu dans le fichier Toto.o et conserve les références des autres modules. :module
:m
:m+*Toto
:m
+Toto
:m
-Toto
:m
-*Toto
Spécifit le contexte pour l'évaluation des expressions :
Active le module non-compilé Toto
Active le module compilé Toto
Désactive le module compilé Toto
Désactive le module non-compilé Toto:browse
:bro
:load
Toto
:info
:i
:load
Toto
:type :t:load
Toto
:!
:!
:!
dir
:main
:ma
:load
Toto
Le chemin de recherche pour la découverte de fichiers source est spécifié avec l'option-
i
La programmation en Haskell se fait dans des modules mis dans des fichiers d'extension hs ou lhs, un module par fichier. Par commodité on utilisera le même nom de module pour le fichier. Les module peuvent être emboités, et le module
Data.Bool.Arbre
correspond alors au fichierData\Bool\Arbre.hs
:help # List the diffent types of commands
:?Spécifit le répertoire courant, ici le répertoire c:\dir1
:cd c:\dir1
:c c:\dir1Spécifier le chemins de recherche des sources, ici les deux répertoires c:\dir1 et c:\dir2
:set -ic:\dir1:c:\dir2
:s -ic:\dir1:c:\dir2Initialise le chemin à vide, (le répertoire courant)
:set -i
:s -i:info map
:i map:browse GHC.Base
:bro GHC.Base
:browse! GHC.Base:module +Complexe
:m +Complex
:cd dir # Change the current directory :load file # Read and eval the contents of a file :reload # Read and eval the contents of the last file:type expr
:t exprDebugging
One tip about debugging. A handy function called trace is in the module Debug.Trace. It was the side effect of printing (to stderr) a string and can use used in the middle of computing values to print out an intermediate result.Debug.Trace.trace :: String -> a -> aIt should be used for debugging only, i.e., not for normally printing of results.f x = g (x+y) (x*y) f x = trace ("f: x=" ++ (show x) ++ ", y= " ++ (show y) ++ "\n") (g (x+y) (x*y))Single line comments are preceded by ``--'' and continue to the end of the line. For example:
Multiline and nested comments begin with {- and end with -}. Thus7 - 4 -- this is subtraction{- this is a multiline comment -}The literate programming style in Haskell allows you to write *.lhs files in which every line is a comment by default. A line is Haskell code only when it begins with ">".
Anything is a comment unless it begins with ">". > main = print [ (n, product[1..n]) | n <- [1..20]]Files that don't have the lhs suffix can be compiled by the compiler by using the -x option.shell> ghc -x lhs haskell.htmlDans l'interpreteur WinGHCi (ou GHCi) Spécifier les répertoires contenant les fichiers sources par exemple c:\dir1 et c:\dir2, à l'aide de la commande suivante :
:set -ic:\dir1:c:\dir2
ou alors déplacer le répertoire courant à l'aide de la commande suivante :
:cd c:\dir1
La programmation en Haskell se fait dans des modules mis dans des fichiers d'extension hs, un module par fichier. Par commodité on utilisera le même nom de module pour le fichier. Les module peuvent être emboités, et le module
Data.Bool.Arbre
correspond alors au fichierData\Bool\Arbre.hs
On crée un répertoire de travail C:\Haskell. Et dans l'intérpréteur, on se positionne sur ce répertoire à l'aides de la commande :
:cd C:\Haskell
On peut exécuter toute commande MSDOS dans l'interpreteur GHCi (ou Hugs) à l'aide de la commande
:! <command>
Et obtenir une aide sur la liste des commandes avec
:?
On charge le fichier du module dans l'interpréteur GHCi (ou Hugs) à l'aide d'une des commandes :
:load C:\Haskell\toto.hs
:load C:\Haskell\toto
:load toto.hs
:load toto
:l C:\Haskell\toto.hs
:l TotoPuis on pourra lancer l'éditeur avec la comande :
:edit
En ayant préalablement définie l'éditeur que l'on souhaite utiliser avec la commande :
:set editor C:\Program Files\SciTE\SciTE.exe
La fonction factoriel
let f(n) = if n==0 then 1 else n*f(n-1)
f(5)
f(10)
=> 120
=> 3628800
Dans WinHugs la commande let doit comprendre une deuxième partie constituée par la commande in et qui délimite la porté du let.
let f(n) = if n==0 then 1 else n*f(n-1) in (f(3),f(5))
=> (6,120)
Le programme principale s'appel main est peut être lancé avec la commande
:main
La fonction factorielle peut se programmer en deux instructions plus lisibles dans un module Std. Les noms de module commence obligatoirement par une majuscule :
Toto.hs
module Std where
f(0) = 1
f(n) = n*f(n-1)On peut charger le module comme suit :
:load Toto
f(5)
f(10)
:main
[1 of 1] Compiling Main ( Toto.hs, interpreted )
=> Ok, modules loaded: Main.
=> 120
=> 3628800
=> 2432902008176640000On peut compiler le module comme suit, mais les fonctionalités non déclarée externe ne sont plus utilisable. On peut alors le rechargé sous forme intérprétée :
:! ghc -c toto.hs
:load toto
f(10)
:main
main
:show modules
:load *toto
f(5)
main
:show modules
=> Ok, modules loaded: Main.
<interactive>:1:0: Not in scope: `f'
=> 2432902008176640000
=> 2432902008176640000
=> Main (toto.hs, toto.o)
[1 of 1] Compiling Main ( toto.hs, interpreted )
=> 120
=> 2432902008176640000
=> Main (toto.hs, interpreted)
do {putStrLn "hello"; return "yes"}
x <- return 42
print x
x
let x = 12
x
let x = error "help!"
print x
=> hello
"yes"
=> 42
=> 42
=> 12
=> *** Exception: help!
let f(x,y) = x*y
f(2,3)
f 2 3
let f x y = x*y
f 2 3
f(2,3)
=> 6
Erreur ! ...
=> 42
Erreur ! ...
let {f op 0 = 0; f op x = x `op` f op (x-1)}
f (+) 3
=> 6
Pour autoriser les passages à la ligne dans un bloc de code soumis à l'interpréteur GHCi, on le commence par
:{
et on le finit par}:
, ainsi la dernière fonction peut s'écrire :
:{
let {f op 0 = 0;
f op x = x `op` f op (x-1)}
}:
f (+) 3
=> 6
:{
let {x=2;
y=[4,56];
a="Bonjour";
w=('a',2);
f x = x**x}
}:
:show bindings
=> a :: [Char] = _
f :: (Floating a) => a -> a = _
w :: (Char, Integer) = ('a',_)
x :: Integer = _
y :: [Integer] = [_,_]
Si vous activer l'option t, l'intérpréteur GHCi montrera le type de chaque variable affectée :
:set +t
let x=("Bonjour",4)
let f n = n^2
let f n = n**2
let (x:y) = [1,2,3]
x
y
=> x :: ([Char], Integer)
=> f :: (Num a) => a -> a
=> g :: (Floating a) => a -> a
=> 1
=> [2,3]
let { f op n [] = n ; f op n (h:t) = h `op` f op n t }
f (+) 0 [1..3]L'inférence des types : L'ordinateur reconnait le type le plus générale de l'object lors de sa construction sans qu'il n'y est besoin que son type soit défini préalablement.
Le polymorphisme paramétrique : une fonction peut s'appliquer à des paramètres avec des types différents d'un appel à l'autre, et produire un résultat de type différents, dépendant des types des paramètres et de la fonction.
Toto.hs
module Md1
where
f(0) = 1
f(n) = n*f(n-1)
Titi.hs
module Md2
where
import Md1
g(x) = x**x/f(x)