我正在玩F#(Visual Studio 2010 beta 1),我写了一个小的控制台脚本,要求用户输入2个数字和一个操作符,然后执行它。 它工作正常,除了一个微小但令人烦恼的事情:有时我的printfn指令被忽略。我在代码中放置了断点,以确定情况确实如此。
代码段:
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
这当然不是完整的来源,但我认为这就足够了。如果您需要完整的来源,请告诉我。
所以我的问题很简单:是什么导致printfn出现这个问题?我做错了吗?
提前致谢, ShdNx
答案 0 :(得分:16)
This page部分解释了发生了什么,但简短而甜蜜的版本是F#如果不接受参数,将在声明上执行任何值。
let read =
printfn "Please enter a number."
System.Console.ReadLine
由于read
不接受任何参数,因此它会在声明时立即执行,并将函数的返回值绑定到标识符read
。
顺便提一下,您的返回值恰好是类型为(unit -> string)
的函数。这是因为如果没有传递所有参数,F#会自动curries functions。 ReadLine
需要一个单位参数,但由于未传递,因此您实际将read
绑定到ReadLine
函数本身。
解决方案如下:
let read() = // read takes one unit parameter
printfn "Please enter a number."
System.Console.ReadLine() // pass paramter to ReadLine method
由于read
接受一个参数,因此每次调用时都会重新评估。另外,我们将参数传递给ReadLine
,否则我们只会将ReadLine
函数作为值返回。
答案 1 :(得分:8)
我知道这可能令人困惑。在您的示例中,printfn
比您想象的要早。即使没有调用read()
,它实际上也会执行,即注释掉最后两行,你仍会看到正在打印的消息。
我认为你的意图是这样的:
let read() =
printfn "Please enter a number.";
System.Console.ReadLine()
这将创建一个“可重用”函数,而不是像在原始示例中那样将函数绑定到标识符。
作为旁注,这里使用分号是可选的,所以你可以写:
let read() =
printfn "Please enter a number."
System.Console.ReadLine()