代码之家  ›  专栏  ›  技术社区  ›  Frank

F#中printfn的类型,静态字符串与动态字符串

f#
  •  24
  • Frank  · 技术社区  · 15 年前

    printfn TextWriterFormat

    在FSI中,我运行以下命令:

    > "hello";;
    val it : string = "hello"
    > printfn "hello";;
    hello
    val it : unit = ()
    

    只是一个普通的字符串并打印它。好的现在我想声明一个变量来包含相同的字符串,并将其打印出来:

    > 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>'
    

    printfn 需要一个常量字符串。我也明白我可以用类似的方法来解决这个问题 printfn "%s" v .

    不过,我想了解一下这里的打字情况。清晰地 "hello" 是一种 string 以及 v printfn printfn "%s" 1 失败。。这当然不能用于动态字符串,但我认为这仅仅是静态情况下编译器方面的一种方便。

    4 回复  |  直到 15 年前
        1
  •  30
  •   kvb    15 年前

    好问题。如果你看 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
    

    如果字符串是静态已知的(如上面的示例所示),那么您仍然可以让编译器推断正确的泛型参数 而不是调用构造函数:

    let (s:Printf.TextWriterFormat<_>) = "hello"
    let (s':Printf.TextWriterFormat<_>) = "Here's an integer: %i"
    let (s'':Printf.TextWriterFormat<_>) = "Float: %f; Bool: %b"
    

    如果字符串是真正动态的(例如,它是从文件读取的),那么您需要显式地使用类型参数并调用构造函数,就像我在前面的示例中所做的那样。

        2
  •  8
  •   Joel Mueller    15 年前

    String.Format 作为常量存储,因为它使代码更清晰:

    String.Format(SomeConstant, arg1, arg2, arg3)
    

    而不是

    String.Format("Some {0} really long {1} and distracting template that uglifies my code {2}...", arg1, arg2, arg3)
    

    但是自从 printf printf

    let formatFunction = sprintf "Some %s really long %i template %i"
    

    创建一个字符串作为输入,返回两个整数。就是说,, string -> int -> int -> string . 它甚至比常量String.Format模板更好,因为它是一个强类型方法,允许我在不内联的情况下重用模板。

    let foo = formatFunction "test" 3 5
    

    我越是使用F#,就越能发现部分函数应用的用法。好东西。

        3
  •  7
  •   Daniel Pratt    15 年前

    我认为说文字值“hello”的类型是不正确的 String printfn "hello" Printf.TextWriterFormat<unit> .

    起初,我觉得奇怪的是,文字字符串值根据其使用位置的上下文会有不同的推断类型,但当然,我们在处理数字文字时已经习惯了这一点,数字文字可能表示整数、小数、浮点等,具体取决于它们出现的位置。

    要声明变量类型,请使用printAdvance的printType来声明它。。。

    let v = "hello" : Printf.TextWriterFormat<unit> in printfn v
    

    Printf.TextWriterFormat 转换 将字符串值转换为必需的类型。。。

    let s = "foo" ;;
    let v = new Printf.TextWriterFormat<unit>(s) in printfn v ;;
    
        4
  •  4
  •   Robert    15 年前

    正如您正确观察到的,printfn函数采用“Printf.textWritePerformat<”a>不是一串。编译器知道如何在常量字符串和“Printf.textWritePerformat<”之间进行转换a>“,但不是在动态字符串和“Printf.textWritePerformat<”之间a>”。