如何编写自定义版本的F#printfn,它接受可变数量的参数?

如何编写自定义版本的F#printfn,它接受可变数量的参数?,f#,printf,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.textWr

我想编写一个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.textWritePerformat)= 让时间=日期时间。现在 让strtime=sprintf“%02d:%02d:%02d”时间。小时时间。分钟时间。秒 printfn“%s%A”strtime fmt
但是printimefn的结果变成了单位,而不是单位。所以它仍然不起作用。我想知道编写自定义printfn函数的正确方法是什么。

这可以通过
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

我想通过将时间的读数移出函数来演示这一点,原因有两个。主要是为了演示如何添加一个或多个不被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!
let tsprintf(dateTime:dateTime)(标记:字符串)=
让dateTimeString=
dateTime.ToString(@“yyyy/MM/dd HH:MM:ss”,
系统。全球化。文化信息。不变量文化)
Printf.ksprintf(有趣的格式字符串->
“[”+dateTimeString+“]”+formatString)
设testTime=DateTime(1987,12,31,11,59,58)
让message=tsprintf testTime“007”“您好%s!”“詹姆斯·邦德”
message.Dump()//LINQPad输出:[1987/12/31 11:59:58]你好,詹姆斯·邦德!
不变量区域性与所选的时间格式字符串相结合,确保您看到的结果与最后的注释中的内容完全一致,无论在哪里或什么地方

我之所以使用DateTime.ToString,是因为它可以按照您想要的方式格式化DateTime。用ksprintf格式化它似乎不是一个好主意


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

如果将最后一行替换为
printf“%s”strtime printfn fmt
,则可以通过分别打印时间和其余数据来实现这一点(在这种情况下不需要类型注释)。我不确定这是否解决了您的问题,因为它将print拆分为两个对printf的调用。这是一个解决方法,但它并不能真正解决问题,因为我没有得到原子函数。但是我相信我已经找到了答案(现在发布)。@VagifAbilov你为什么要删除你自己的答案?
Printf.
是FSharp.Core的一个非常有用的部分,对大多数开发者来说都是未知的部分。但是,我想知道您是否应该移动lambda函数中的
DateTime.Now
,以便在打印时进行计算。否则,会导致意外(?)时间的风险diff@Justanothermetaprogrammer:这实际上取决于用例。无论哪种方式,现在显示的<代码>和正在显示的数据之间都会有一个小的延迟。
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!