2009-06-24 14 views
7

stavo giocando in giro con F # (Visual Studio 2010 beta 1), e ho scritto un piccolo script console che ha chiesto all'utente di inserire 2 numeri e un operatore e quindi eseguito esso. Funziona bene, a parte una piccola cosa, ma fastidiosa: a volte le mie istruzioni printfn sono ignorate. Ho inserito i breakpoint nel codice per vedere che è davvero così.F # strano problema printfn

Il frammento di codice:

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 

Questa non è la fonte completa, naturalmente, ma penso che sarà sufficiente. Se hai bisogno della fonte completa fammelo sapere.

Quindi la mia domanda è molto semplice: che cosa provoca questo problema con printfn? Sto facendo qualcosa di sbagliato?

Grazie in anticipo, ShdNx

risposta

15

This page ha una spiegazione parziale di quello che sta succedendo, ma la versione breve e dolce è che F # sarà eseguire qualsiasi valore sulla dichiarazione se non accetta parametri.

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

Poiché read non prende alcun parametro, la sua eseguite immediatamente dichiarazione e lega il valore di ritorno della funzione all'identificatore read.

Per inciso, il valore del ritorno sembra essere una funzione del tipo (unit -> string). Questo risulta perché F # curries functions automaticamente se non vengono passati tutti i loro parametri. ReadLine si aspetta un parametro unità, ma dato che non è passata, in realtà si legano read alla funzione ReadLine stesso.

La soluzione è la seguente:

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

Da read prende un parametro, la sua rivalutati ogni volta la sua chiamata. Inoltre, stiamo passando un parametro a ReadLine, altrimenti restituiremo la funzione ReadLine come valore.

+0

La ringrazio molto! Sfortunatamente Ray era più veloce, quindi accettai la sua risposta. Ma sono comunque molto contento che tu l'abbia chiarito. Grazie ancora! – ShdNx

+0

Sono d'accordo! +1 per una spiegazione più chiara! –

7

Capisco che questo può essere fonte di confusione. Nel tuo esempio, printfn viene eseguito prima di quanto pensi. Veramente eseguito anche senza la chiamata a read(), ad esempio, commenta le ultime due righe e vedrai comunque un messaggio in fase di stampa.

Credo che la vostra intenzione è qualcosa di simile:

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

Questo creerà una funzione di "riutilizzabile" invece di legare una funzione per un identificatore come nel tuo esempio originale.

Come sidenote, l'uso di punti e virgola qui è opzionale in modo da poter solo scrivere:

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

La ringrazio molto, credo di aver capito ora! Io uso un punto e virgola perché aggiungo automaticamente un punto e virgola dopo ogni riga e mi dà fastidio se non riesco a vedere alcun ... :-) – ShdNx