Précédent   Forum du club des développeurs et IT Pro > Autres langages > Langages fonctionnels > Haskell
Haskell Forum d'entraide sur la programmation en langage fonctionnel Haskell
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 31/08/2011, 12h37   #1
Nel_Star
Invité régulier
 
Inscription : juillet 2011
Messages : 10
Détails du profil
Informations forums :
Inscription : juillet 2011
Messages : 10
Points : 5
Points : 5
Par défaut foldl à la place d'un paterm matching

Bonjour,

Jaime bien les folds parce que ça utilise les lambdas et la récursion terminale (je crois).
J'aimerais savoir si on peut remplacer ma fonction filt' par un fold comme plus haut dans mon code (script simplissime que j'ai codé pour un script bash). Ou même reduire ce code(j'apprends donc je me preocuperais de la lisibilité une autre fois)

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rev' :: String -> String --fonction reverse...
rev' = foldl (\acc x -> x : acc) []

patio' :: String -> Integer --dis si la chaine contient un '/'
patio' = foldl (\acc x -> if x == '/' then  1 + acc else acc) 0

filt' :: String -> String -- vire le debut de la chaine jusqu'au premier '/'
filt' [] = []
filt' (x:xs) = if x == '/' then xs else filt' xs

rparsePah :: String -> String --main
rparsePah xs = if patio' xs /= 0 then rev' $ filt' $ rev' xs else xs

main = do interact rparsePah
En attendant une réponse, bonne journée.
Nel_Star est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/08/2011, 19h37   #2
Troll OCaml
Invité de passage
 
Homme
Collégien
Inscription : août 2011
Messages : 2
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Collégien
Secteur : Agroalimentaire - Agriculture

Informations forums :
Inscription : août 2011
Messages : 2
Points : 2
Points : 2
Si j'ai bien compris, voilà qui fera l'affaire :

Code :
1
2
filt' = reverse . snd . foldl (\acc x -> if fst acc then (True, x : snd acc) else if x == '/' then (True, []) else (False, [])) (False, [])
Troll OCaml est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/09/2011, 01h22   #3
Nel_Star
Invité régulier
 
Inscription : juillet 2011
Messages : 10
Détails du profil
Informations forums :
Inscription : juillet 2011
Messages : 10
Points : 5
Points : 5
Bonsoir,
C'est bien ce que je cherchais à faire mais pour dire vrai je ne comprends pas tout à fait.

Que vient faire le () dans :
Merci d'avoir répondu.
Nel_Star est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/09/2011, 09h12   #4
Troll OCaml
Invité de passage
 
Homme
Collégien
Inscription : août 2011
Messages : 2
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Collégien
Secteur : Agroalimentaire - Agriculture

Informations forums :
Inscription : août 2011
Messages : 2
Points : 2
Points : 2
Citation:
Envoyé par Nel_Star Voir le message
Bonsoir,
C'est bien ce que je cherchais à faire mais pour dire vrai je ne comprends pas tout à fait.

Que vient faire le () dans :
Merci d'avoir répondu.
Rien, c'était de la bêtise.
Troll OCaml est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/09/2011, 12h37   #5
Nel_Star
Invité régulier
 
Inscription : juillet 2011
Messages : 10
Détails du profil
Informations forums :
Inscription : juillet 2011
Messages : 10
Points : 5
Points : 5
Ça marche.
J'ai mis résolu. Je te remercie ça faisait bien deux jours que je me cassais la tête.
Tchao.
Nel_Star est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/09/2011, 22h26   #6
Twinside
Membre du Club
 
Inscription : octobre 2007
Messages : 34
Détails du profil
Informations forums :
Inscription : octobre 2007
Messages : 34
Points : 43
Points : 43
Attention, bien que très pratique, il ne faut pas utiliser les folds à tord et à travers
Citation:
Envoyé par Nel_Star Voir le message
Bonjour,
Code :
1
2
3
rev' :: String -> String --fonction reverse...
rev' = foldl (\acc x -> x : acc) []
Effectivement dans ce cas là, c'est une bonne utilisation du fold, également utilisé dans le prélude Haskell pour la fonction reverse (l'implémentation est visible en cliquant sur 'Sources')
Citation:
Envoyé par Nel_Star Voir le message
Bonjour,
Code :
1
2
3
patio' :: String -> Integer --dis si la chaine contient un '/'
patio' = foldl (\acc x -> if x == '/' then  1 + acc else acc) 0
Là par exemple, c'est une très mauvaise utilisation, le but est de faire une recherche d'élément, et toute la liste est parcourue pour trouver ta réponse, alors que la recherche pourrait être arrêté avant. Une manière plus directe de faire (et qui est également récursive terminale) :
Code :
1
2
3
4
5
chercheElement :: Char -> String -> Bool
chercheElement _ [] = False
chercheElement a (x:xs) | a == x = True
                        | otherwise = chercheElement a xs
Il existe d'autre manière de faire, notamment comme l'implémentation de la fonction elem, mais elles mettent en oeuvre des fonctions d'ordre supérieure et (surtout), s'appuie sur l'évaluation paresseuse de Haskell (chose que ne permet pas le foldl dans ce cas là).

Code :
1
2
3
4
filt' :: String -> String -- vire le debut de la chaine jusqu'au premier '/'
filt' [] = []
filt' (x:xs) = if x == '/' then xs else filt' xs
Cette fonction ne bénéficierai pas d'être implémenté avec un fold, sachant qu'on veut se débarrasser du début de la liste, la dropper, on peut utiliser la fonction dropWhile
Code :
1
2
3
4
5
6
7
8
rparsePath :: String -> String
rparsePath str
    | not $ '/' `elem` str = str
    | otherwise = reverse . dropUntilFirstSlash $ reverse str
           where dropUntilFirstSlash = reverse . safeTail . dropWhile (/= '/') . reverse
                 safeTail (x:xs) = xs
                 safeTail _ = []
En bonus, la version simplifié du programme :
Code :
1
2
3
4
5
6
import System.FilePath( dropFileName )
main :: IO ()
main = interact (safeTail . dropFileName)
           where safeTail (x:xs) = xs
                 safeTail _ = []
Il ne faut pas hésiter à fouiner la librairie existante en Haskell il y a très souvent moyen de se simplifier la vie
Twinside est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/09/2011, 00h57   #7
Nel_Star
Invité régulier
 
Inscription : juillet 2011
Messages : 10
Détails du profil
Informations forums :
Inscription : juillet 2011
Messages : 10
Points : 5
Points : 5
Bonsoir,
Petite mise à jour. Aprés avoir lu vos conseils, j'ai legerement modifié mon script.
Qui ressemble maintenant à :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import System.Environment

nonPresent :: Char -> String -> Bool
nonPresent a str = not $ elem a str

rparsePath :: String -> String
rparsePath str
    | nonPresent '/' str = str
    | otherwise = reverse . safeTail . dropWhile (/= '/') . reverse $ str

rparseFile :: String -> String
rparseFile str
    | nonPresent '/' str = str
    | otherwise = reverse . takeWhile (/= '/') . reverse $ str

rparseExt :: String -> String
rparseExt str
    | nonPresent '.' str = str
    | otherwise = reverse . takeWhile (/= '.') . reverse $ rparseFile str

rparseName :: String -> String
rparseName str
    | nonPresent '.' str = str
    | otherwise = reverse . safeTail . dropWhile (/= '.') . reverse $ rparseFile str

safeTail :: String -> String
safeTail (x:xs) = xs
safeTail _ = []

argFilt :: [String] -> String
argFilt cmdLineArgs
    | first == "-p" = rparsePath secnd
    | first == "-ne" = rparseFile secnd
    | first == "-n" = rparseName secnd
    | first == "-e" = rparseExt secnd
    | first == "-pn" = rparsePath secnd ++ '/':rparseName secnd
    | otherwise = "bad option argument"
  where secnd = head $ tail cmdLineArgs
        first = head cmdLineArgs

arg_verif :: [String] -> Bool
arg_verif a = a /= [] && (length a) >= 2

main :: IO ()
main = do
        args <- getArgs
        putStrLn $ if arg_verif args then argFilt args else "nothing to process"
Bon le code est plutot facile à lire mais en gros soit blob = /dummy/path/lol.test, le fait d'appeler le script avec l'option -p donnera /dummy/path, avec -n lol, -e test, -ne lol.test, -pn /dummy/path/lol.

En fait j'avais à l'origine besoin d'interfacer(premier post) vim avec ghc,gcc, pdflatex. Et la variable % de vim demande un peu de tweak.

Alors je sais qu'il existe des solution plus rapides(j'en connais quelque unes) avec sed, awk, python, etc... Mais je prefere utiliser des outils que je maitrise.

Dernierement, utiliser System.FilePath m'a donné des erreurs de types à en avoir mal à la tête(j'ai fini par comprendre...) et donc je m'en passe ici.

La encore vos comentaires sont les bienvenus. Salute.
Nel_Star est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/09/2011, 11h51   #8
Twinside
Membre du Club
 
Inscription : octobre 2007
Messages : 34
Détails du profil
Informations forums :
Inscription : octobre 2007
Messages : 34
Points : 43
Points : 43
Code :
1
2
3
arg_verif :: [String] -> Bool
arg_verif a = a /= [] && (length a) >= 2
est strictement équivalent à
Code :
1
2
3
arg_verif :: [String] -> Bool
arg_verif a = length a >= 2
vu que si une liste est vide, sa taille est 0. Et il est plus intéressant d'utiliser le pattern matching plutôt que head & tail lorsque cela est possible :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import System.Environment
import System.FilePath

safeTail :: String -> String
safeTail (x:xs) = xs
safeTail _ = []

optionTodo :: [(String, FilePath -> FilePath)]
optionTodo = [("-p", dropFileName)
             ,("-n", dropExtension . takeFileName)
             ,("-ne", takeFileName)
             ,("-e", safeTail . takeExtension)
             ,("-pn", \a -> dropExtension a ++ ".")
             ]

main :: IO ()
main = do
    args <- getArgs
    case args of
        (opt: name: _) -> case opt `lookup` optionTodo of
                Nothing -> putStrLn $ "Wrong argument " ++ opt
                Just act -> putStrLn $ act name
        _ -> putStrLn "Wrong number of argument"
En matchant sur args, on évite beaucoup de problème et de risques d'exception.
Twinside est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 20/09/2011, 13h14   #9
Nel_Star
Invité régulier
 
Inscription : juillet 2011
Messages : 10
Détails du profil
Informations forums :
Inscription : juillet 2011
Messages : 10
Points : 5
Points : 5
Bonjour,
Merci Twinside. J'ai eu du mal à comprendre ce que voulait dire Nothing et Just. Du coup pas mal de choses dont je passais à coté.

J'ai légérement modifié le script pour qu'il soit plus à mon gout :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import System.Environment
import System.FilePath

safeTail :: String -> String
safeTail (x:xs) = xs
safeTail _ = []

optionTodo :: [(String, FilePath -> FilePath)]
optionTodo = [("-p", reverse.safeTail.reverse.dropFileName)
             ,("-n", dropExtension . takeFileName)
             ,("-ne", takeFileName)
             ,("-e", safeTail . takeExtension)
             ,("-pn", dropExtension)
             ]

main :: IO ()
main = do
    args <- getArgs
    case args of
        (opt: name: _) -> case opt `lookup` optionTodo of
                Nothing -> putStrLn $ "Wrong argument " ++ opt
                Just act -> putStrLn $ act name
        _ -> putStrLn "Wrong number of argument"
Voila. Salutations.

EDIT : En fait j'ai un probleme avec la ligne 22. A quoi sert act (je crois que ça initie l'action qui est donnée par optionTodo)?
Nel_Star est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Cette discussion est résolue.
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 00h52.


 
 
 
 
Partenaires

Hébergement Web