包装printf并仍然采取参数

时间:2014-11-18 21:26:05

标签: f#

/// Colored printf
let cprintf c fmt = 
    Printf.kprintf 
        (fun s -> 
            let old = System.Console.ForegroundColor 
            try 
              System.Console.ForegroundColor <- c;
              System.Console.Write s
            finally
              System.Console.ForegroundColor <- old) 
        fmt

// Colored printfn
let cprintfn c fmt = 
    cprintf c fmt
    printfn ""

使用the above stolen code

cprintfn ConsoleColor.Yellow "This works"

但是

cprintfn ConsoleColor.Yellow "This %s" "doesn't"
(cprintfn ConsoleColor.Yellow) "still %s" "doesn't"
(cprintfn ConsoleColor.Yellow "still %s") "doesn't"
cprintfn ConsoleColor.Yellow ("still %s" "doesn't")

我知道这与Printf.TextWriterFormat<_> vs简单字符串有关,但即使在fmt中指定cprintf c (fmt:Printf.TextWriterFormat<_>)的类型,我也无法让kprintf部分工作。我阅读了一些与日志记录相关的答案,但我仍然无法弄明白。 cprintf是否可以采用格式参数?

1 个答案:

答案 0 :(得分:6)

首先,您的cprintf函数非常好,您可以使用可变数量的参数。问题仅出在cprintfn函数:

cprintf System.ConsoleColor.Red "%s %d" "hi" 42

问题在于,当您定义使用格式化字符串的函数时,您始终需要使用部分应用程序。那就是你的功能必须是以下形式:

let myprintf fmt = 
  <whatever>
  otherprintf <whatever> fmt

重要的是otherprintf <whatever> fmt必须是身体的最后一部分,并且必须以fmt作为最后一个参数。这样,当otherprintf需要更多参数(由fmt指定)时,这些参数会自动传播并成为myprintf的参数。

这意味着根据cprintfn定义cprintf实际上非常棘手。但是你总是可以定义一个功能更强大的辅助函数,让你同时做到这两点:

let cprintfWith endl c fmt = 
    Printf.kprintf 
        (fun s -> 
            let old = System.Console.ForegroundColor 
            try 
              System.Console.ForegroundColor <- c;
              System.Console.Write (s + endl)
            finally
              System.Console.ForegroundColor <- old) 
        fmt

let cprintf c fmt = cprintfWith "" c fmt
let cprintfn c fmt = cprintfWith "\n" c fmt