IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Haskell Discussion :

Interpreter des arguments en ligne de commande


Sujet :

Haskell

  1. #1
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut Interpreter des arguments en ligne de commande
    Bonjour

    Ayant crééer un petit programme Haskell (compilé) j'aimerais pourvoir en exécuter certaines opérations en ligne de commande.
    Par exemple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    splot :: [Float -> Float] -> Coord -> Coord -> IO ()
    splot [f] dom rang = ....
    crée un fichier présentant la courbe d'une fonction f : dom --> rang
    Remarque: splot [f1, f2 ...] dom rang trace les courbes de l'ensemble des fonctions.

    Afin d'importer les paramètres en ligne de commande, j'utilise ( avec System.Environment )

    Par exemple , si j'introduis dans terminal

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
       ../bidule   splot     "[\x->x*sin(x)]"    "(0,3)"   "(0,2)"
    On a args!!0 = "[splot]"
    args!!1 = "[\x->x*sin(x)]"
    args!!2 = "(0,3)"
    args!!3 = "(0,2)"

    Mon idée est de rassembler tout cela dans une chaine unique et de l'interpréter.

    Dans notre exemple, la chaine est obtenue par consatSpace args

    qui donne, comme on peut s'y attendre: "splot [\x->x*sin(x)] (0,3) (0,2)"

    Pour l'interpréter, j'essaye d'utiliser hint qui doit être ajouté au fichier cabal ( build-depends: base, hint )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    main=do
         args <- getArgs
         runInterpreter $ do { setImports ["Prelude"]; eval $ consatSpaced args }
    La compilation s'effectue sans message d'erreur, et la commande dans terminal ne produit également aucun message ,
    mais le fichier graphique attendu n'est pas créé.

    Il y a peut-être un problème avec l'interprétation du caractère "\" utilisé pour lambda. J'ai aussi essayé "\\" mais sans succès...
    Ou est l'erreur ?

  2. #2
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Voila quelques informations complémentaires ( j'ai aussi corrigé un détail dans ma présentation ci-dessus ).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    main=do
         args <- getArgs
         progName <- getProgName
         putStrLn "le nom du programme est:"
         putStrLn progName
         putStrLn "les arguments sont:"
         mapM putStrLn args
         putStrLn "le premier terme est"
         putStrLn $ show (head args)
         putStrLn "le deuxième terme est"
         putStrLn $ show (args!!1)
     
         putStrLn "l'instruction reconstituée est"
         putStrLn $ consatSpaced args
         putStrLn "ce qui devrait créer un fichier graphique contenant la courbe de la ( ou des ) fonctions introduites ..."
         runInterpreter $ do { setImports ["Prelude"]; eval $ consatSpaced args }
    En introduisant dans terminal:

    piya:~ /Users/HaskellTMOnLine1/build/HaskellTMOnLine1/TMOnLine1 splot "[\x->x*sin(x)]" " (0,3)" "(0,2)"

    j'obtiens :

    1. le nom du programme est:
    2. TMOnLine1
    3. les arguments sont:
    4. splot
    5. [\x->x*sin(x)]
    6. (0,3)
    7. (0,2)
    8. le premier terme est
    9. "splot"
    10. le deuxième terme est
    11. "[\\x->x*sin(x)]"
    12. l'instruction reconstituée est
    13. splot [\x->x*sin(x)] (0,3) (0,2)
    14. ce qui devrait créer un fichier graphique contenant la courbe de la ( ou des ) fonctions introduites ...


    La raison pour laquelle j'introduit les domaines entre guillemets ( "(0,3)" et "(0,2)" ) et que sinon terminal me retourne "syntax error near unexpected token `('"

    Tout à l'aire de se passer correctement mais le fichier n'est pas produit.
    Par contre si je copie-colle résultat (13. ci-dessus ) dans gchi, cela produit le fichier correctement.

  3. #3
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Pas d'erreurs ? C'est vite dit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    main = do
      ...
      result <- runInterpreter $ do { setImports ["Prelude"]; eval $ consatSpaced args }
      case result of
        Left error -> putStrLn (show error)
        Right valueAsString -> putStrLn valueAsString
    Deux suggestions :
    • Ajouter "Splot" en argument de setImports.
    • Corriger consatSpaced en concatSpaced (= Data.List.intercalate " " ?) parce que c'est trop moche sinon.

  4. #4
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Merci Chatanga pour tes suggestions.

    [*]Corriger consatSpaced en concatSpaced (= Data.List.intercalate " " ?) parce que c'est trop moche sinon.

    --> Ok j'ai remplacé mon con vilain satSpaced args par intercalate " " args , mais c'est plutôt de la cosmétique...

    [*]Ajouter "Splot" en argument de setImports.

    --> Donc utiliser runInterpreter $ do { setImports ["Prelude","Splot"]; eval $ intercalate " " args } ?

    mais "Splot" n'existe pas ( seulement "splot" existe ). Il faut définier un module de ce nom ?
    Je n'ai pas trouvé d'information sur ce point ( à part quelques vagues questions comme ici )

  5. #5
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Je ne connais pas Splot, mais après avoir jeté un oeil à sa documentation, je pense que ton objectif est plutôt le suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import Data.List
    import System.Environment
    import System.Process
    import Language.Haskell.Interpreter
     
    import Lib
     
    main :: IO ()
    main = do
      args <- getArgs
      result <- runInterpreter $ do
        setImports ["Prelude"]
        let points = ["0.4", "0.1", "0.2"]
        eval $ "map (\\x -> (x, x*sin(x))) [" ++ intercalate "," points ++ "]"
      case result of
        Left error -> putStrLn (show error)
        Right valueAsString -> do
          putStrLn $ "values: " ++ valueAsString
          (exitCode, out, err) <- readProcessWithExitCode "splot" [valueAsString] ""
          putStrLn $ "exit code: " ++ show exitCode
          putStrLn $ "out: " ++ out
          putStrLn $ "err: " ++ err
    En gros, tu sembles mélanger la partie "production dynamique des données via une fonction passée en paramètre" et la partie "appel d'un outil externe Splot qui se trouve par simple coïncidence être codé en Haskell". Par aeilleurs, vu la description de Splot, je ne suis pas convaincu que ce soit le bon type d'outil et utiliser un grapher plus généraliste tel que plot serait probablement plus approprié (mais je n'ai peut-être pas bien compris où tu vas).

  6. #6
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Bonsoir Chatanga , merci pour ta suggestion.

    Je ne connais pas Splot, mais après avoir jeté un d'oeil à sa documentation, ...
    -> Malheureuse coïncidence ! le Splot sur lequel tu as trouvé de la doc ( et dont j'ignorais l'existence ) n'a rien à voir là dedans.

    Mon splot, dont la déclarations de type est splot :: [Float -> Float] -> Coord -> Coord -> IO ()
    est une fonction Haskell qui génère un fichier d'extension tm (Gnu-TeXmacs file) présentant les courbes d'une sensemble ( set, d'où le s devant plot ) de fonctions à une variable réelle. Si j'arrivais à l'exécuter en ligne de commande, je pourrais aussi le faire depuis l'éditeur TeXmacs, ce qui serait plus confortable.

    En gros, tu sembles mélanger la partie "production dynamique des données via une fonction passée en paramètre" et la partie "appel d'un outil externe Splot qui se trouve par simple coïncidence être codé en Haskell". Par ailleurs, vu la description de Splot, je ne suis pas convaincu que ce soit le bon type d'outil et utiliser un grapher plus généraliste tel que plot serait probablement plus approprié (mais je n'ai peut-être pas bien compris où tu vas)
    Je ne sais pas si ta conclusion s'applique aussi à ma fonction splot, il y a peut-être quelque-chose de cet ordre là,
    car ma fonction plot n'est pas pure ( elle recours à IO pour créeer un fichier ), et il est peut-être naïf de supposer qu'elle va être gentiment
    exécutée via runInterpreter.

  7. #7
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Quelle idée aussi d'appeler une fonction "splot". Pourquoi pas "pfuit" ou "plouf" tant qu'on y est !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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 Data.List
    import System.Environm
    import Language.Haskell.Interpreter
     
    import Lib
    import Splot
     
    main :: IO ()
    main = do
      args <- getArgs
      result <- runInterpreter $ do
        loadModules ["src/Splot.hs"]
        setImports ["Prelude", "Splot"]
        interpret "splot (\\x -> x*sin(x)) ((0,0) :: Coord) ((10,10) :: Coord)" (as :: IO ())
      case result of
        Left error -> putStrLn (errorString error)
        Right action -> action -- Il reste effectivement à exécuter IO ().
     
    errorString :: InterpreterError -> String
    errorString (WontCompile es) = intercalate "\n" (header : map unbox es)
      where
        header = "ERROR: Won't compile:"
        unbox (GhcError e) = e
    errorString e = show e

  8. #8
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Ca Marche !!
    Merci Beaucoup Chatanga cela m'a vraiment aidé !

    ( Au passage, je n'ai pas réussi à importer Lib, mais cela ne semble pas nécessaire )

  9. #9
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut Execution d'un programme Haskell compilé sur un autre ordinateur
    Bonsoir, j'ai hésité à créer une nouvelle rubrique pour cette nouvelle question,
    mais comme j'ignore dans quelle mesure elle est liée à ce qui précède, je préfère la poser ici.

    Je viens d’essayer le lancer ma petite application ( celle de la discussion ci-dessus ) depuis un autre ordinateur ( mac du meme type que celui utilisé pour la compilation ).

    J’obtiens le message d’erreur suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TMOnline3: /usr/local/Cellar/ghc/8.0.2/lib/ghc-8.0.2/settings: openFile: does not exist (No such file or directory)
    Ce message apparait lors de l’exécution de la partie faisant appel à runInterpreter et à un module ( loadModules ["src/Splot.hs"] )

    On dirait que pour cette partie, le contenu du fichier binaire ne suffit pas , et que d'autres éléments doivent être installés séparément.

    Ma question est :

    Existe-t-il un moyen de compiler l'application de façon à incorporer proprement tout ce qui est nécessaire à une exécution correcte
    ( sur une machine du même type ) ?

    Si oui : comment ?
    Si non : quelle est l'installation minimale qui doit être proposée en complément ?

    Merci d'avance,
    Bertrand

  10. #10
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Sans être absolument catégorique, je dirais que ça n’a rien à voir avec le code que tu veux interpréter, mais avec l’interpréteur lui-même. En l’occurrence, le package hint qui offre le module Language.Haskell.Interpreter et qui ne fait qu’encapsuler l’API GHC. Si tu as déjà tenté d’utiliser un « EDI » (Emacs, Leksah, IntelliJ, etc.), tu as sûrement dû constater les limites d’outils tels que ghc-mod et assimilés qui exploitent l’API de GHC, laquelle évolue entre chaque version. Travailler sur plusieurs projets utilisant des versions différentes de GHC oblige de fait à avoir autant de versions de ces outils (vive Stack au passage). C’est très probablement ce qui t’arrive en tentant d’exécuter un programme avec une version de GHC différente de celle utilisée pour sa compilation.

  11. #11
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Travailler sur plusieurs projets utilisant des versions différentes de GHC oblige de fait à avoir autant de versions de ces outils.
    En effet... J’utilise Kronos-Haskell ( qui a sa propre version de GHC ) mais seulement pour les petits dialogues avec ghci, et IntelliJ pour rassembler les lignes de code et pour la compilation.

    C’est très probablement ce qui t’arrive en tentant d’exécuter un programme avec une version de GHC différente de celle utilisée pour sa compilation.
    En fait j’essaye d’exécuter ce programme compilé sur une machine qui n’a aucune version de GHC.
    Je constate que cela ne fonctionne pas, alors que pour des programmes plus rudimentaires cela semble marcher correctement.
    J’aimerais partager ce petit programme avec des collègues enseignants en maths, de la façon la plus simple possible. Comment faire ?
    Il existe des compilateurs en ligne, pourraient-ils l’utiliser si je leur donne le fichier source ?
    Ma question principale est: dans quel(s) cas ( et pourquoi ) est-il indispensable d’installer un framework pour exécuter un programme Haskell compilé et quelle en est l’installation minimale ?

    Meilleures salutations de l’Inde.
    Bertrand

  12. #12
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Ce n'est pas une histoire de framework...

    Ton programme permet à l'utilisateur d'entrer n'importe quelle expression Haskell valide et de l'exécuter, pourquoi es-tu surpris qu'il soit nécessaire d'avoir un compilateur Haskell sur la machine pour cela ? Comment penses-tu que l'expression Haskell soit transformé en code exécutable/interprété sans cela ?

    Hint en tant que librairie est juste un emballage autour de GHC pour permettre à un programme Haskell d'interpréter du code Haskell à l'exécution, il est donc normal qu'il nécessite d'avoir GHC pour fonctionner. Tu pourrais distribuer GHC avec ton programme mais cela est rarement une option très appréciée (GHC est très lourd). Alternativement tu pourrais revoir les ambitions de ton programme à la baisse et limiter ce qu'il est possible d'entrer à un sous-ensemble plus raisonnable à interpréter sans la machinerie GHC (par exemple si tu as juste besoin d'évaluer des expressions mathématiques simples, tu as une libraire pour ça, simple mais facilement extensible si d'autres fonctions sont nécessaires).

    Si tu choisis de garder ta forme actuelle, intéresse-toi à stack, cela te permettra de donner des consignes d'installation simples qui installeront GHC si nécessaire, et une seule fois (!) pour tes collègues. La deuxième option est probablement la plus raisonnable mais demandera un peu plus de travail, n'hésite pas à demander de l'aide si tu es bloqué ou si tu veux étendre les capacités de mathExpr.

  13. #13
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Merci Jedai, je comprend mieux à présent.

    C’est en effet un tout petit sous-ensemble des expressions Haskell qu’il s’agit d’interpréter.
    Je croyais ( vraiment naïvement ! ) que l'instruction loadModules ["module.hs"] restreignait l’ensemble des expressions à celle du module en question lors de la compilation.

    Les deux solutions ( stack et la librairie mathexpr ) sont intéressantes, il est bien probable que je repasse avec d’autres questions à leur sujet...

  14. #14
    Membre averti
    Avatar de Chatanga
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    211
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 211
    Points : 346
    Points
    346
    Par défaut
    Tous les packages dont dépend un programme Haskell sont liés statiquement à ce dernier. Il est possible de compiler un package sous forme dynamique pour pouvoir le lier ensuite dynamiquement, mais ce n’est pas le cas par défaut. C’est au passage la raison pour laquelle les exécutables Haskell sont généralement volumineux. Les dépendances natives, elles, sont liées dynamiquement.

    Dans ton cas, le package hlint est donc lié statiquement à ton programme, mais il continue à dépendre « dynamiquement » de GHC. Alors, évidemment, si la machine cible n’a pas GHC, comme le souligne Jedai, ça ne risque pas de fonctionner. Le fait que tu compiles avec GHC un programme qui dépend de GHC n’est ici qu’une coïncidence.

    Il reste néanmoins possible de demander à GHC de lier statiquement les dépendances natives pourvu que tu les possèdes sous la forme appropriée. Dans ton cas, ça ne fonctionnerait cependant même pas, car hint ne dépend qu’indirectement de GHC via le package ghc-paths (en autres) et non sous forme de bibliothèques. Jedai a très bien résumé les solutions qui s’offrent maintenant à toi pour échanger facilement ton programme.

  15. #15
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Merci encore Chatanga et Jedai, vos explications se complètent, c’est clair !
    J’ai installé mathexpr via terminal :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     http://hackage.haskell.org/package/mathexpr-0.3.0.0/mathexpr-0.3.0.0.tar.gz
    Par contre je n’arrive pas à l’importer depuis intellij ( qui n'en connais pas son chemin d’accès ).

    donne le message

    ghc: error: Failed to load interface for ‘Data.MathExpr’
    Use -v to see a list of the files searched for.


    Je pense réinstaller complètement Haskell et Intellij avec stack en suivant les instructions de https://gist.github.com/androidfred/...343c529d32acd8
    De quoi occuper quelques pluvieuses soirées de mousson…

  16. #16
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Alternativement tu pourrais revoir les ambitions de ton programme à la baisse et limiter ce qu'il est possible d'entrer à un sous-ensemble plus raisonnable à interpréter sans la machinerie GHC (par exemple si tu as juste besoin d'évaluer des expressions mathématiques simples, tu as une libraire pour ça, simple mais facilement extensible si d'autres fonctions sont nécessaires).
    Merci encore pour ce précieux conseil !

    mathexpr package est en effet une très bonne solution.
    A présent mon application compilée est exécutée par des ordinateurs du même type, et la taille du fichier est 2.6 Mo au lieu des 105 Mo de la version précédente ( celle qui encapsulait GHC ).

    La deuxième option est probablement la plus raisonnable mais demandera un peu plus de travail, n'hésite pas à demander de l'aide si tu es bloqué ou si tu veux étendre les capacités de mathExpr.
    La première option consistait à installer Stack et à tout lui laisse gérer.
    Dans mon cas cette expérience n’a pas été très convainquante, car l’installation de Stack ( probablement mal configurée ) est par elle-même un problème à gérer.

    Cordialement,
    Bertrand

  17. #17
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    The mathexpr package permet d'interpréter une expression contenant le nom d'une fonction "f x" (String) et une valeur val (Double) grâce à l'instruction evaluate.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    evaluate :: Settings -> String -> [(String, Double)] -> Double
    evaluate def "f x" [("x", val )]
    donne le résultat de f val.

    Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    evaluate def "x" [("x", 7)]
    donne 7.

    Cependant il y a deux situations dans lesquelles cet interpréteur ne fonctionne pas bien:

    1) Lorsque abs val < 0.1

    par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     evaluate def "x" [("x", 0.02)]
    donne : src/Numeric/MathExpr.hs:Non-exhaustive patterns in function helper

    2) Si la fonction est une somme écrite sous une de ces formes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    evaluate def "x"  [( "sin x + 1" ,  2 )]
    evaluate def "x"  [( "(sin x) + 1" , 2 )]
    evaluate def "x"  [( "sin (x) + 1" , 2 )]
    cela donne :Prelude.read: no parse

    Solutions :

    1) Dû à la notation scientifique de Haskell, ainsi 0.02 = 2e-2 que l'interpréteur de comprend pas.
    J'ai trouvé une solution pour résoudre ce problème.

    2) A moins d'échanger les termes de la somme ( en effet ça marche avec "1 + sin x"] ! )
    je ne vois vraiment pas comment faire.

  18. #18
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    extrait1 du code de MathExpr.hs


    1) les opérateurs de base (avec leur élément neutre)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    defaultOperators = [ ('+', 0, (+)) , ('-', 0, (-)) , ('*', 1, (*)) ,  ('/', 1, (/)) , ('^', 2 , (**)) ]
    2) evaluate utilise une fonction auxiliaire helper, qui envisage différents cas possibles.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    evaluate settings expr vars =
            let postfix = toPostfix settings expr
                replaced = replaceVariables postfix vars
            in helper (tokenize replaced) []
            where
              ops = operators settings
              fns = functions settings
     
              helper :: [String] -> [String] -> Double
              -- negative numbers come in the form ["num", "-"]
              helper [] [o] = read o
              helper [n, "-"] [] = negate . read $ n
              helper ["-"] [n] = negate . read $ n
              helper (c:cs) os
                | c == "-" && length os < 2 =
                    let result :: Double = negate . read . head $ cs
                    in helper (tail cs) $ (show result) : os
                | isOperator c && length os >= 2 =
                    let result = (operatorFunction c) (read . head . tail $ os) (read . head $ os) 
                    in helper cs $ (show result) : drop 2 os
                | isFunction c =
                    let result = (function c) (read . head $ os)
                    in helper cs $ (show result) : tail os
                | otherwise = helper cs (c:os)

    Je ne vois pas à quel niveau intervenir.

    Exemples d'expressions non interprétées :

    "sin x + 1"
    "sin x - 1"
    "sin x * 1"
    "sin x / 1"

    par contre

    "sin x + x"
    "sin x + cos x"

    sont interprétées correctement.

    "1 + sin x" est interprétée mais pas "exp (1 + sin x)"

  19. #19
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Ok, j'ai jeté un coup d’œil au code et je suppose qu'une modification récente a dû casser l'une des fonctions et qu'il n'a testé que sur des cas où ça marchait. (c'est pour ça qu'une suite de tests correcte est toujours importante, damnit !)

    Mais de toute façon après avoir regardé de plus près cette librairie est sérieusement cassée... Elle fait elle-même sa tambouille pour le parsing au lieu d'utiliser une librairie correcte faite pour ça.

    Du coup j'ai écris un petit module qui fait la même chose de façon nettement plus robuste :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    module Numeric.MiniCalc (parseAndEvaluate, parseAndEvaluateWith, parseMaths, evaluate, expr, Expr(..), defaultFunctions) where
     
    import qualified Data.Map.Strict as M
    import Text.Parsec
    import Text.Parsec.Expr
    import qualified Text.Parsec.Token as P
    import Text.Parsec.Language (haskellDef)
     
    type Env a = M.Map String a
     
    data Expr a =
        Num a
      | Var String
      | Unary (a -> a) (Expr a)
      | Binary (a -> a -> a) (Expr a) (Expr a)
      | Function String (Expr a)
     
    expr funcs = exprs
      where exprs = buildExpressionParser (map function funcs : table) term
                    <?> "expression"
            term =  parens exprs
                    <|> Num <$> number
                    <|> Var <$> identifier
                    <?> "simple expression"
     
    table = [ [binary "^" (**) AssocRight]
            , [prefix "-" negate, prefix "+" id ]
            , [binary "*" (*) AssocLeft, binary "/" (/) AssocLeft ]
            , [binary "+" (+) AssocLeft, binary "-" (-)   AssocLeft ]
            ]
     
    binary    name fun assoc = Infix (do{ reservedOp name; return (Binary fun) }) assoc
    prefix    name fun       = Prefix (do{ reservedOp name; return (Unary fun) })
    postfix   name fun       = Postfix (do{ reservedOp name; return (Unary fun) })
    function  name           = Prefix (do{ reservedOp name; return (Function name) })
     
    lexer       = P.makeTokenParser haskellDef
     
    parens      = P.parens lexer
    identifier  = P.identifier lexer
    number      = either fromInteger id <$> P.naturalOrFloat lexer
    reservedOp  = P.reservedOp lexer
     
    evaluate :: Env (a->a) -> Env a -> Expr a -> a
    evaluate _     _   (Num n)           = n
    evaluate _     env (Var s)           = M.findWithDefault (error $ "Cannot find " ++ s ++ " in the environment") s env
    evaluate funcs env (Unary f e)       = f (evaluate funcs env e)
    evaluate funcs env (Binary f e1 e2)  = f (evaluate funcs env e1) (evaluate funcs env e2)
    evaluate funcs env (Function name e) = let func = M.findWithDefault (error $ "Cannot find " ++ name ++ " in the functions") name funcs
                                           in func (evaluate funcs env e)
     
    parseMaths :: [String] -> String -> Either ParseError (Expr Double)
    parseMaths funcs s = parse (expr funcs) "Maths Expression" s
     
    parseAndEvaluateWith :: [(String, Double->Double)] -> [(String, Double)] -> String -> Double
    parseAndEvaluateWith funcs env s = case parseMaths (map fst funcs) s of
      Left err -> error $ show err
      Right e  -> evaluate (M.fromList funcs) (M.fromList env) e
     
    parseAndEvaluate :: String -> Double
    parseAndEvaluate = parseAndEvaluateWith defaultFunctions []
     
     
    defaultFunctions = [("cos", cos), ("sin", sin), ("tan", tan)
                       ,("exp", exp), ("log", log)
                       ,("abs", abs), ("sqrt", sqrt)]
    Il devrait faire l'affaire et comprend toutes les expressions que tu pourrais écrire en Haskell avec le peu de pièces définies ici (les opérateurs classiques et les fonctions que tu lui fournis avec defaultFunctions comme exemple).

    J'ai vérifié qu'il fonctionne avec toutes tes expressions. Il utilise parsec (>= 3.1) et containers (>= 0.5).

    Tu l'utilises avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    parseAndEvaluate "cos (3.14/2)"
    si tu n'as pas de variables et que les fonctions par défaut te suffisent ou :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    parseAndEvaluateWith defaultFunctions [("x", 5)]  "exp(-1 + sin x)"
    si tu veux avoir des variables ou pour pouvoir rajouter des fonctions. (définit peut-être un raccourci pour "parseAndEvaluateWith tesFonctions")

    --
    Jedaï

  20. #20
    Membre à l'essai
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2014
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2014
    Messages : 37
    Points : 22
    Points
    22
    Par défaut
    Ca marche parfaitement, je suis très impressionné !
    Merci beaucoup Jedaï , c'est formidable

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [Débutant] Créer un exe avec des arguments de ligne de commande
    Par Gigli dans le forum VB.NET
    Réponses: 1
    Dernier message: 06/02/2012, 10h35
  2. Comment passer des arguments en ligne de commande?
    Par Razgriz dans le forum NetBeans
    Réponses: 1
    Dernier message: 11/04/2007, 12h11
  3. Réponses: 6
    Dernier message: 19/10/2005, 13h10
  4. taille max des arguments en ligne de commande
    Par clepape dans le forum Langage
    Réponses: 2
    Dernier message: 08/10/2005, 14h18
  5. Réponses: 3
    Dernier message: 07/04/2003, 21h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo