如何编写采用可变数量参数的F#printfn自定义版本?

时间:2018-08-29 08:09:31

标签: f# printf

我想编写F#printfn函数的扩展版本,该功能除了显示文本外还显示当前时间。像这样:

let printimefn fmt =
    let time = DateTime.Now
    let strtime = sprintf "%02d:%02d:%02d" time.Hour time.Minute time.Second
    printfn "%s %A"  strtime fmt

不幸的是,这无法按预期进行。 “ fmt”参数丢失其类型Printf.TextWriterFormat <'T>。

我可以通过使用类型注释来强制其类型:

let printimefn (fmt : Printf.TextWriterFormat<'T>) =
    let time = DateTime.Now
    let strtime = sprintf "%02d:%02d:%02d" time.Hour time.Minute time.Second
    printfn "%s %A"  strtime fmt

但是,printimefn的结果变成单位,而不是'T。因此它仍然不起作用。我想知道编写自定义printfn函数的正确方法是什么。

2 个答案:

答案 0 :(得分:4)

这可以通过Printf.ksprintf

完成
let printimefn fmt =
    let time = DateTime.Now
    Printf.ksprintf (
        fun s ->
            printfn "%02d:%02d:%02d %s" time.Hour time.Minute time.Second s)
        fmt

答案 1 :(得分:0)

出于两个原因,我想通过将时间的读数移出函数来演示这一点。主要是为了演示如何添加一个或多个ksprintf不使用的参数,还因为我们应该具有一个没有副作用的函数。如果要在函数中读取时间,则可以创建一个包装器来读取时间,然后调用以下函数。

首先,有两个未传递给ksprintf的显式参数。然后隐式跟随格式字符串和格式字符串所需的任何隐式参数。

let tsprintf (dateTime: DateTime) (tag: string) =
    let dateTimeString =
        dateTime.ToString (@"yyyy/MM/dd HH:mm:ss",
            System.Globalization.CultureInfo.InvariantCulture)
    Printf.ksprintf (fun formatString ->
        "[" + dateTimeString + " <" + tag + ">] " + formatString)

let testTime = DateTime(1987, 12, 31, 11, 59, 58)

let message = tsprintf testTime "007" "Hello %s!" "James Bond"

message.Dump() // LINQPad output : [1987/12/31 11:59:58 <007>] Hello James Bond!

将InvariantCulture与选定的时间格式字符串结合使用,可以确保无论在何处或什么地方,您看到的结果都是末尾注释中的内容。

我使用DateTime.ToString的原因是,无论您使用哪种方式,它都会格式化DateTime。在ksprintf中格式化它似乎不是一个好主意。

在tsprintf中显式声明了ksprintf不使用的参数。 ksprintf的格式字符串和以下参数未显式声明,但它们遵循所有显式声明的参数。