2009-06-24 12 views
7

Ich spielte mit F # (Visual Studio 2010 Beta 1) herum und schrieb ein kleines Konsolenskript, das den Benutzer aufforderte, 2 Zahlen und einen Operator einzugeben und dann auszuführen. Es funktioniert gut, abgesehen von einem kleinen, aber lästigen Ding: manchmal werden meine Printfn Anweisungen ignoriert. Ich habe Haltepunkte im Code platziert, um zu sehen, dass dies tatsächlich der Fall ist.F # seltsames printfn Problem

Der Code-Schnipsel:

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 

Dies ist nicht die vollständige Quelle natürlich, aber ich denke, dass genug sein werde. Wenn Sie die komplette Quelle benötigen, lassen Sie es mich wissen.

Also meine Frage ist ziemlich einfach: Was verursacht dieses Problem mit printfn? Mache ich etwas falsch?

Vielen Dank im Voraus, ShdNx

Antwort

15

This page eine teilweise Erklärung hat, was los ist, aber die kurze und süße Version ist, dass F # wird Führen Sie einen beliebigen Wert für die Deklaration aus, wenn er keine Parameter akzeptiert.

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

Da read nimmt keine Parameter, seine unmittelbar nach der Deklaration ausgeführt und bindet den Rückgabewert der Funktion dem Identifikator read.

Übrigens ist Ihr Rückgabewert eine Funktion mit dem Typ (unit -> string). Dies resultiert, weil F # automatisch curries functions, wenn sie nicht alle ihre Parameter übergeben werden. ReadLine erwartet einen Einheitsparameter, aber da er nicht übergeben wird, binden Sie read tatsächlich an die ReadLine-Funktion.

Die Lösung ist wie folgt:

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

Da read nimmt einen Parameter, seine neu bewertet jedes Mal seinen Namen. Außerdem übergeben wir einen Parameter an ReadLine, andernfalls geben wir einfach die ReadLine-Funktion als Wert zurück.

+0

Vielen Dank! Leider war Ray schneller, also akzeptierte ich seine Antwort. Aber ich bin immer noch sehr froh, dass du das klar gestellt hast. Danke noch einmal! – ShdNx

+0

Ich stimme zu! +1 für eine klarere Erklärung! –

7

Ich verstehe, dass dies verwirrend sein kann. In Ihrem Beispiel läuft printfn früher als Sie denken. Es wird tatsächlich sogar ohne den Anruf zu read() ausgeführt, d. H. Die letzten zwei Zeilen kommentieren und Sie werden immer noch eine Nachricht gedruckt sehen.

Ich denke, Ihre Absicht, so etwas wie diese:

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

Diese eine „wiederverwendbar“ -Funktion anstelle der Bindung eine Funktion eine Kennung wie in Ihrem ursprünglichen Beispiel zu schaffen.

Als Nebenbemerkung, die Verwendung von Semikolons hier ist optional, so können Sie einfach schreiben:

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

Vielen Dank, ich glaube ich verstehe es jetzt! Ich benutze Semikolons, weil ich nach jeder Zeile automatisch ein Semikolon hinzufüge und es stört mich wenn ich nichts sehen kann ... :-) – ShdNx