16.1.1 事件函数

728 篇文章 1 订阅


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。


16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。




16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。



16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

16.1.1 事件函数

    通过显式提供的回调函数使用事件,并不是非常声明性的,我们是用命令式添加事件处理程序,且把整个行为打包在回调函数里面。那么,怎样把事件看成是更具声明性方式呢?我们已经看到过,声明性代码中的一个重要技术是使用高阶函数。最好的例子是处理列表的函数,比如List.filter、List.map 或 List.fold。如果我们有一个来自文件系统监视程序(称为 watcherEvents)的事件列表,可以把这些代码拆分成两个部分。第一部分把事件的源格式转换成适宜打印的格式,这部分可以用声明式风格来写,使用高阶函数处理列表。第二部分可以用简单的命令式代码,打印信息。这种方法的主要好处是,大部分代码用声明式风格来写。特别是,筛选的代码将如下所示:


let renamedVisible = 
  watcherEvents |> List.filter isNotHidden

    这个代码段使用的 isNotHidden 函数,是作为参数传递给筛选列表的函数。第二部分可以使用 List.iter 来打印每个列表中的项。这只是我们熟悉的使用列表方式,但清单 16.2 显示了完整的示例,正是用这种模式来处理事件的。我们可以把事件当成与列表类似:两者都表示序列值,所不同的是,事件中的值不是立即可用,而是每当事件触发时,才会出现一个新值。这个事件参数的序列可以用类似处理集合的方式进行筛选。Observable.filter 函数创建一个事件,当源事件生成的值匹配给定的断言时触发。

Listing 16.2 Filtering events using the Observable.filter function (F# Interactive)

> let renamedVisible = 
     fileWatcher.Renamed |> Observable.filter isNotHidden 
;; 
val renamedVisible : System.IObservable<RenamedEventArgs> 

> renamedVisible |> Observable.add (fun fse –> 
     printfn "%s renamed to %s" fse.OldFullPath fse.FullPath);; 
val it : unit


    第一个命令筛选事件,与筛选列表值的方式非常相似,返回的事件侦听文件系统监视程序的事件,当文件重命名时,它使用指定的筛选函数,测试由事件携带的值是否应该忽略。如果筛选函数返回 true,会触发结果事件;否则,将忽略当前发生的事件。

    表示返回事件值的类型,需要更多的解释,我们得到了的值,是 .NET 4.0 新的类型 System.IObservable<'T>,它已添加了处理对象的统一方法,即,需要通知其他状态改变的对象。在上一节,我们写的 F# 中的事件,其值为 IEvent<'T> 类型,所以,你可能想知道,为什么我们在这里没有看到 IEvent<'T>。让我们先看一下清单的其余部分,再详细讨论这两种类型些之间的关系。
    在列表中的最后一个命令注册了 lambda 函数,使用筛选的事件,打印有关重命名文件的信息。我们使用另一个处理事件的函数,Observable.add,这个函数的行为,与 IEvent <'T> 类型的 Add 方法类似,我们先前使用过。当使用 IObservable<'T > 时,我们将把整个事件处理代码,用高阶函数,写成一个流操作。现在,让我们来更详细看看,在 F# 中,事件和事件值的表示方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值