我刚刚开始在Mono中使用F#,并且出现了以下问题,我无法理解.查找资料printfn,并TextWriterFormat没有带来任何启示,所以我想我要问在这里.
在FSI中,我执行以下操作:
> "hello";;
val it : string = "hello"
> printfn "hello";;
hello
val it : unit = ()
Run Code Online (Sandbox Code Playgroud)
只是一个普通的字符串并打印它.精细.现在我想声明一个变量来包含相同的字符串并打印它:
> let v = "hello" in printfn v ;;
let v = "hello" in printfn v ;;
---------------------------^
\...\stdin(22,28): error FS0001: The type 'string' is not compatible with the type 'Printf.TextWriterFormat<'a>'
Run Code Online (Sandbox Code Playgroud)
我从阅读中理解,printfn需要一个恒定的字符串.我也明白,我可以解决这个问题printfn "%s" v.
但是,我想了解这里输入的内容.显然,"hello"类型string也是如此v.为什么会出现类型问题?有printfn什么特别的吗?据我所知,编译器已经对第一个字符串的参数执行了类型检查,这样就printfn "%s" 1失败了......这当然不适用于动态字符串,但我认为这只是编译器方面的一个方便.静态案例.
kvb*_*kvb 26
好问题.如果查看类型printfn,即Printf.TextWriterFormat<'a> -> 'a,您将看到编译器TextWriterFormat在编译时自动将字符串强制转换为对象,从而推断出相应的类型参数'a.如果您想使用printfn动态字符串,您可以自己执行该转换:
let s = Printf.TextWriterFormat<unit>("hello")
printfn s
let s' = Printf.TextWriterFormat<int -> unit>("Here's an integer: %i")
printfn s' 10
let s'' = Printf.TextWriterFormat<float -> bool -> unit>("Float: %f; Bool: %b")
printfn s'' 1.0 true
Run Code Online (Sandbox Code Playgroud)
如果字符串是静态已知的(如上例所示),那么您仍然可以让编译器推断出正确的泛型参数,TextWriterFormat而不是调用构造函数:
let (s:Printf.TextWriterFormat<_>) = "hello"
let (s':Printf.TextWriterFormat<_>) = "Here's an integer: %i"
let (s'':Printf.TextWriterFormat<_>) = "Float: %f; Bool: %b"
Run Code Online (Sandbox Code Playgroud)
如果字符串是真正动态的(例如,它是从文件中读取的),那么您将需要显式使用类型参数并像前面的示例中那样调用构造函数.
我不认为说文字值"hello" String在上下文中使用时是正确的printfn "hello".在此上下文中,编译器将文字值的类型推断为Printf.TextWriterFormat<unit>.
起初我觉得奇怪的是,文字字符串值会根据其使用位置的上下文而具有不同的推断类型,但当然我们在处理数字文字时会习惯这种情况,这可能代表整数,小数,浮点数等等,取决于它们出现的位置.
如果要在通过printfn使用它之前声明变量,可以使用显式类型声明它...
let v = "hello" : Printf.TextWriterFormat<unit> in printfn v
Run Code Online (Sandbox Code Playgroud)
...或者你可以使用构造函数Printf.TextWriterFormat将普通的 String值转换为必要的类型......
let s = "foo" ;;
let v = new Printf.TextWriterFormat<unit>(s) in printfn v ;;
Run Code Online (Sandbox Code Playgroud)
这只与你的问题有些相关,但我认为这是一个方便的伎俩.在C#中,我经常使用模板字符串String.Format作为常量存储,因为它使代码更清晰:
String.Format(SomeConstant, arg1, arg2, arg3)
Run Code Online (Sandbox Code Playgroud)
代替...
String.Format("Some {0} really long {1} and distracting template that uglifies my code {2}...", arg1, arg2, arg3)
Run Code Online (Sandbox Code Playgroud)
但由于printf方法系列坚持使用文字字符串而不是值,我最初认为如果我想使用,我不能在F#中使用这种方法printf.但后来我意识到F#有更好的东西 - 部分功能应用.
let formatFunction = sprintf "Some %s really long %i template %i"
Run Code Online (Sandbox Code Playgroud)
这只是创建了一个函数,它接受一个字符串和两个整数作为输入,并返回一个字符串.也就是说,string -> int -> int -> string.它甚至比一个常量的String.Format模板更好,因为它是一个强类型的方法,它允许我重新使用模板而不包括内联.
let foo = formatFunction "test" 3 5
Run Code Online (Sandbox Code Playgroud)
我使用F#越多,我发现部分功能应用的用途就越多.好东西.