2017-03-15 91 views
4

我想知道是否有一个很好的方法来定义一个函数captureOutput,它需要一个函数f,该函数可能包含打印语句,并返回打印的所有内容f。例如,在f#中捕获打印语句

let f x = print "%s" x 
let op = captureOutput (f "Hello World") 

val op : string = "Hello World" 

我在想,也许有一个很好的办法做到这一点异步地Console.ReadLine(),但我一直没能上班什么的。

干杯

编辑:

基于陀Soikin的评论,下面的代码做什么,我想:

let captureOutput f x = 
    let newOut = new IO.StringWriter() 
    Console.SetOut(newOut) 
    f x 
    Console.SetOut(Console.Out) 
    newOut.ToString() 
+3

你有没有试过['Console.SetOut'](https://msdn.microsoft.com/en-us/library/system.console.setout(v = vs.110).aspx)? –

+0

太棒了,非常感谢您的参考!这对我来说很完美。 – ira

+1

很高兴我能帮到你。但这实质上是一种黑客行为,谨慎使用。看到我的答案。 –

回答

5

您可以通过Console.SetOut临时替换标准输出写入器。

但要小心:这种替换还会影响在其他线程上执行的代码,并捕获它们的输出,并与函数的输出混合使用。这基本上就是所谓的“黑客”。

如果这仅仅是一个永远不会变得更复杂的非常小的工具,那么这很好。但是,这绝不应该成为生产系统的一部分。如果您正在开发复杂的东西的一部分,我建议改变本身的功能,参数化它在打印功能:

type Printer = abstract member print (fmt: StringFormat<'T, unit>) : 'T 

let captureOutput f = 
    let mutable output = "" 
    let print s = output <- output + s 
    f { new Printer with member _.print fmt = kprintf print fmt } 
    output 

let f x (p: Printer) = p.print "%s" x 
let op = captureOutput (f "Hello World") 

(本例中必须使用的接口,因为没有它的print功能将失去泛型)

+0

太好了,谢谢。事实上,这是一个非常小的实用程序(为我正在上课的课程安排学生作业),所以这个解决方案是完美的。 – ira

3

实现@ FyodorSoikin的建议(我只后看到我写了这个):

let captureOutput f = 
    use writer = new StringWriter() 
    use restoreOut = 
     let origOut = Console.Out 
     { new IDisposable with member __.Dispose() = Console.SetOut origOut } 
    Console.SetOut writer 
    f() 
    writer.ToString() 

let f x() = printf "%s" x 
let op = captureOutput (f "Hello World") 

(N.b.必须向f添加额外参数,以便可以部分应用– captureOutput必须采用函数,而不是值)。

IDisposable的对象表达式用于包装清理,以便对f的调用是异常安全的。

+0

嗨ildjarn,谢谢你的回复!在看到您的回复之前,我还会根据@ FyodorSoikin的建议在主要答案中编写我自己的解决方案,它们非常相似。 – ira

+0

'IDisposable'黑客看起来有点冗长,你试试...... finally' :-) –

+0

@FyodorSoikin:F#是我使用的唯一一种try-finally语言;我不是一个真正的粉丝,在编写代码时很少想到它。 /耸肩: - ] – ildjarn