码迷,mamicode.com
首页 > 其他好文 > 详细

12.7.2 创建日志记录计算

时间:2015-02-02 15:57:48      阅读:264      评论:0      收藏:0      [点我收藏+]

标签:f#   函数编程   实用函数编程   计算生成器   计算表达式   

12.7.2 创建日志记录计算

 

清单 12.24 首先实现两个辅助函数,用于读写控制台,且两者还会把消息写入日志,所以,它们将括在 log 计算块中。为了显示如何组合非标准计算,我们在第三个函数中,使用了两个函数。在前面的示例中,我们使用 let! 基本操作,在清单 12.24 还引入了 do!。

 

清单 12.24 使用计算表达式的日志 (F# Interactive)

> let write(s) = log {    [1] <-- 写字符串到控制台和日志

     do!logMessage("writing: " + s)

     Console.Write(s) }

val write : string ->Logging<unit>

 

> let read() = log {

     do!logMessage("reading")

     returnConsole.ReadLine() }

val read : unit -> Logging<string>

 

> let testIt() = log {

     do! logMessage("starting")  [2] <-- 调用日志函数基本操作

     do!write("Enter name: ")  [3]<-- 调用另一个计算表达式

     let! name = read()  [4] <-- 使用自定义的值绑定

     return "Hello" + name + "!" }

val testIt : unit ->Logging<string>

 

> let res = testIt();;

Enter name: Tomas

 

> let (Log(msg, logs)) = res;;

val msg : string = "Hello Tomas!"

val logs : string list =["starting"; "writing: Enter name:"; "reading"]

 

如果运行清单中的代码,它会等待控制台的输入,这在 Visual Studio 中的 F# Interactive 环境下,不能运行,所以,需要在独立命令行控制台中运行代码。我们在几个地方用到了新的 do! 基本操作,调用返回 Logging<unit> 的函数。在这里,我们使用了非标准绑定,执行 Bind 成员,因为我们想把日志记录消息连接起来。我们可以忽略实际值,因为它是 unit,这正是 do! 基本操作的行为。事实上,当我们使用 do! f () 时,它是 let! () = f() 的缩写,即,使用自定义绑定,忽略返回 unit 的值。

实现计算生成器时,我们添加了一个成员 Zero,它在清单 12.24 的幕后使用。不返回任何值的计算[1],F# 编译器会自动使用 Zero 的结果,作为整体结果。在讨论编译器把代码转换为方法调用时,我们会看到,这个成员是如何使用的。

如果我们看一下清单中的类型签名,就会发现,所有函数的结果类型都是计算类型(Logging<‘T>),与我们前面实现的 logMessage 函数的结果类型相同。这说明,我们有两种方法写非标准计算类型函数。可以直接生成计算类型(像我们在 logMessage 函数中所做的),或者使用计算表达式。第一种情况主要用于写基本操作;第二种方法用于写组合基本操作或函数的代码。

从 testIt 函数中可以发现,计算表达式具有可组合的特性。首先,使用 do! 结构,直接调用实现的基本操作函数[2];写到屏幕(和日志),是使用计算表达式实现的,而我们调用它的方式完全相同[3];当我们调用返回值并将写到日志中的函数,是使用有 let! 关键字的自定义的绑定[4]。

事实上,没有必要了解编译器如何将计算表达转换成方法调用,但是,如果你有兴起,清单 12.25 显示了前面清单转换后的代码,包括 Zero 成员的使用和 do! 基本操作的转换。

 

清单 12.25 日志示例的转换版本 (F#)

let write(s) =

  log.Bind(logMessage("writing:" + s), fun () –>

    Console.Write(s)

    log.Zero())  [1] <-- 自动使用 zero 作为结果

 

let read() =

 log.Bind(logMessage("reading"), fun () –>

   log.Return(Console.ReadLine()))

 

let testIt() =

 log.Bind(logMessage("starting"), fun () –>  | [2]

    log.Bind(write("Entername: "), fun () –>  | 把多个绑定转换成

    log.Bind(read(), funname –>           | 嵌套调用

     log.Return("Hello " + name + "!"))))

 

Zero 基本操作只在 write 函数中使用[1],因为这是唯一从函数中不返回任何结果的地方。在其他两个函数中,最里面的调用是 Return 成员,参数为一个简单的值,把它打包到 LoggingValue <‘T> 类型中,不包含任何日志消息。

可以发现,转换计算表达式时,每个 do! 或 let! 的使用,都用调用 Bind 成员替换[2]。如果我们回忆早前有关序列表达式的讨论,就会看到现在的相似之处,在序列表达式中,每一个 for 循环转换成对 Seq.collect 的调用。我们可以更进一步地类比,因为 Return 基本操作对应于创建只包含一个元素的序列,对序列表达式的Zero 基本操作可能返回空序列。

还有一点需要我们突出关注。如果再看看清单 12.24 的原始代码,就会发现,它看起来就像普通 F# 代码,只是加了几个感叹号(!),把普通的 F# 代码打包成计算表达式,非常容易。

12.7.2 创建日志记录计算

标签:f#   函数编程   实用函数编程   计算生成器   计算表达式   

原文地址:http://blog.csdn.net/hadstj/article/details/43408333

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!