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# 代码打包成计算表达式,非常容易。

时间: 2024-10-13 22:23:52

12.7.2 创建日志记录计算的相关文章

12.7.1 创建日志记录的计算

这个计算将产生一个值,并能够将消息写入到本地日志记录缓冲区.这样,计算的结果将成为一个值,和包含消息的字符串列表.同样,我们使用只有一个识别器的差别联合,表示这个类型: type Logging<'T> = | Log of 'T * list<string> 这个类型非常类似于我们先前讨论的 ValueWrapper <'a> 示例,只是加上了一个 F# 列表,表示写到日志的消息.现在,我们已有了类型,就可以实现计算生成器了.通常,我们需要实现 Bind 和 Retu

PHP创建日志记录(已封装)

1 <?php 2 3 class Logs{ 4 private $_filepath; //文件路径 5 private $_filename; //文件名 6 private $_filehandle; //文件引擎 7 8 9 public function Logs($dir = null,$filename = null){ 10 11 $this->_filepath = empty($dir) ? '': $dir; 12 $this->_filename = empty

springmvc+log4j操作日志记录,详细配置

没有接触过的,先了解一下:log4j教程 部分内容来:log4j教程 感谢! 需要导入包: log包:log4j-12.17.jar 第一步:web.xml配置 <!-- log4j配置,文件路径,因为是跟随项目启动 --> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/log4j.xml</param-value>

12.7 给计算增加日志记录

日志通常可以使用全局可变状态实现.然而,如果我们想要避免使用全局可变状态,并保持程序的纯函数性,那又该如何呢?我们有一个选择,就是把日志记录器的状态作为额外的参数值,传递我们要调用的每个函数.实现可能非常困难(想象一下,如果我们决定将另一个参数添加到这个状态中!). 要解决这个问题,我们可以创建自定义计算类型,启用了日志记录,把日志记录器的状态隐藏在计算类型内.这类似于在无任何副作用的纯函数式语言中, Haskell 使用的技术,嵌入处理状态(比如,文件系统).我们要实现的示例依赖于这样的事实,

log4j教程 12、日志记录到数据库

log4j API提供 org.apache.log4j.jdbc.JDBCAppender 对象,它能够将日志信息在指定的数据库. JDBCAppender 配置: Property 描述 bufferSize 设置缓冲区的大小.默认大小为1 driver 设置驱动程序类为指定的字符串.如果没有指定驱动程序类,默认为sun.jdbc.odbc.JdbcOdbcDriver layout 设置要使用的布局.默认布局是org.apache.log4j.PatternLayout password

C# 创建自己的日志记录类 源码

调试及发布程序时,经常需要将一些信息输出保存,这里写了一个自己的日志记录类,记录信息更方便了.需要的话还可以进行更多的扩展,比如记录异常信息等. using System; using System.IO; namespace WindowsFormsApplication1 { public static class LogerHelper { #region 创建日志 ///-----------------------------------------------------------

Linux下定时切割nginx日志并删除指定天数前的日志记录

nginx的log日志分为access.log和error.log:其中access.log 记录了哪些用户.哪些页面以及用户浏览器.ip和其他的访问信息:error.log则是记录服务器错误日志. error.log日志的形式如下: 201.158.69.116 - - [03/Jan/2013:21:17:20 -0600] fwf[-] tip[-] 127.0.0.1:9000 0.007 0.007 MX pythontab.com GET /html/test.html HTTP/1

Linux服务器上创建日志服务器和FTP服务器

参考地址: http://www.111cn.net/sys/CentOS/81133.htm https://www.cnblogs.com/laoxiajiadeyun/p/9943742.html https://blog.51cto.com/liqingbiao/2119953 https://www.cnblogs.com/hasayaki/archive/2013/01/24/2874889.html 在 Linux 上配置一个 syslog 服务器 syslog服务器可以用作一个网

转:使用log4net完成程序异常日志记录(使用SQLite数据库记录和普通文本记录)

http://www.cnblogs.com/kyo-yo/archive/2010/06/11/use-log4net-to-log-exception.html 在前端时间开发的时候由于需要将异常保存到数据库中,所以就到网上搜了下专门的日志记录工具,一搜果然很多,比如:log4net,NLog,EntLib Logging等等,但是还是log4net名气最大,所以就下载下来试用了一番,果然很方便,其涵盖了所有常用的日志记录方式具体的可以看下表: AdoNetAppender 将日志记录到数据