2009-06-24 7 views
7

Je jouais avec F # (Visual Studio 2010 beta 1), et j'ai écrit un petit script de console qui demandait à l'utilisateur d'entrer 2 chiffres et un opérateur, puis l'a exécuté. Cela fonctionne bien, mis à part une petite chose, mais ennuyeuse: parfois mes instructions printfn sont ignorées. J'ai placé des points d'arrêt dans le code pour voir que c'est effectivement le cas.F # étrange problème printfn

L'extrait de code:

let convert (source : string) = 
    try System.Int32.Parse(source) 
    with :? System.FormatException -> 
     printfn "'%s' is not a number!" source; 
     waitForExitKey(); 
     exit 1 

let read = 
    printfn "Please enter a number."; 
    System.Console.ReadLine 

let num1 : int = read() |> convert // the printfn in the read function is run... 
let num2 : int = read() |> convert // ... but here is ignored 

Ce n'est pas la source complète bien sûr, mais je pense que ce sera suffisant. Si vous avez besoin de la source complète, faites le moi savoir.

Donc, ma question est assez simple: quelles sont les causes de ce problème avec printfn? Est-ce que je fais quelque chose de mal?

Merci à l'avance, ShdNx

Répondre

15

This page a une explication partielle de ce qui se passe, mais la version courte et douce est que F # sera exécuter n'importe quelle valeur sur la déclaration si elle ne prend pas de paramètres.

let read = 
    printfn "Please enter a number." 
    System.Console.ReadLine 

Depuis read ne prend pas de paramètres, sa exécutée immédiatement sur la déclaration et lie la valeur de retour de la fonction à l'identificateur read. Incidemment, votre valeur de retour se trouve être une fonction avec le type (unit -> string). Cela se produit parce que F # curries functions automatiquement s'ils ne sont pas passés tous leurs paramètres. ReadLine attend un paramètre d'unité, mais puisqu'il n'est pas transmis, vous liez effectivement read à la fonction ReadLine elle-même.

La solution est la suivante:

let read() = // read takes one unit parameter 
    printfn "Please enter a number." 
    System.Console.ReadLine() // pass paramter to ReadLine method 

Depuis read prend un paramètre, sa re-évalué chaque fois que son appelé. En outre, nous passons un paramètre à ReadLine, sinon nous allons simplement renvoyer la fonction ReadLine en tant que valeur.

+0

Merci beaucoup! Malheureusement, Ray était plus rapide, alors j'ai accepté sa réponse. Mais je suis toujours très content que tu l'aies clarifié. Merci encore! – ShdNx

+0

Je suis d'accord! +1 pour une explication plus claire! –

7

Je comprends que cela peut être source de confusion. Dans votre exemple, printfn s'exécute plus tôt que vous ne le pensez. Il s'exécutera même sans appel à read(), c'est-à-dire, commenter les deux dernières lignes et vous verrez toujours un message en cours d'impression.

Je pense que votre intention est quelque chose comme ceci:

let read() = 
    printfn "Please enter a number."; 
    System.Console.ReadLine() 

Cela va créer une fonction « réutilisable » au lieu de lier une fonction à un identifiant comme dans votre exemple original.

En sidenote, l'utilisation des points-virgules ici est facultative et vous pouvez simplement écrire:

let read() = 
    printfn "Please enter a number." 
    System.Console.ReadLine() 
+0

Merci beaucoup, je pense que je le comprends maintenant! J'utilise des points-virgules car j'ajoute automatiquement un point-virgule après chaque ligne et cela me dérange si je ne vois pas ... :-) – ShdNx