第七章 F# 库(五)

事件(Microsoft.FSharp.Control.Event)模块

可以把 F# 中的事件看做是函数的集合,能够通过函数调用来触发。其思想是,函数本身注册成事件,即函数的集合,等待事件发生的通知;然后,触发函数发出事件已经发生的通知,引发所有添加到事件中的函数被执行。

我们将讨论事件模块中的下列功能:

创建和处理事件:使用 create 和 add 函数来创建和处理事件的基础;

筛选(filter)函数:筛选进入事件中数据的函数;

分区(partition)函数:把进入事件中数据拆分成两部分的函数;

映射(map)函数:在数据到达事件处理程序之前进行映射的函数。

创建和处理事件

第一个示例看一个简单的事件,通过调用事件(Event)对象的构造函数来创建,应该传递一个类型参数给构造,表示想要的事件类型;这个对象包含触发(Trigger)函数和表示事件本身的发布(Publish)属性。使用事件的 Publish 属性的 Add 函数添加处理程序方法,最后,用 Trigger 函数触发这个事件:

let event = new Event<string>()

event.Publish.Add(fun x -> printfn"%s" x)

event.Trigger "hello"

代码的运行结果如下:

hello

除了这个事件的基本功能之外,F# 的事件模块还提供了大量的函数,来筛选和分区事件,进行细粒度控制哪些数据传递给哪些事件处理程序。

筛选(filter)函数

下面的示例演示了如何使用事件模块的筛选函数,使数据在到达事件处理程序之前被筛选。在这个示例中,筛选那些以 H 开头的字符串数据发送给事件处理程序。

let event = new Event<string>()

let newEvent = event.Publish |>Event.filter (fun x -> x.StartsWith("H"))

newEvent.Add(fun x -> printfn "newevent: %s" x)

event.Trigger "Harry"

event.Trigger "Jane"

event.Trigger "Hillary"

event.Trigger "John"

event.Trigger "Henry"

代码的运行结果如下:

new event: Harry

new event: Hillary

new event: Henry

分区(partition)函数

事件模块的分区函数与筛选函数相似,只是它返回了两个事件,一个是分区函数返回假所触发的数据,另一个是分区函数返回真所触发的数据。下面的示例有演示:

let event = new Event<string>()

let hData, nonHData = event.Publish |>Event.partition (fun x -> true)

let x = Event.partition

hData.Add(fun x -> printfn "H data:%s" x)

nonHData.Add(fun x -> printfn "NoneH data: %s" x)

event.Trigger "Harry"

event.Trigger "Jane"

event.Trigger "Hillary"

event.Trigger "John"

event.Trigger "Henry"

代码的运行结果如下:

H data: Harry

None H data: Jane

H data: Hillary

None H data: John

H data: Henry

映射(map)函数

在数据到达事件处理程序之前进行转换也是可能的,这要乃至事件模块中提供的映射函数。下面的示例演示了如何使用:

let event = new Event<string>()

let newEvent = event.Publish |>Event.map (fun x -> "Mapped data: " + x)

newEvent.Add(fun x -> printfn"%s" x)

event.Trigger "Harry"

event.Trigger "Sally"

代码的运行结果如下:

Mapped data: Harry

Mapped data: Sally

这一节只对 F# 中的事件提供了一个概览,在第八章讨论用户界面编程时会有更详细的介绍,因为那才是事件的最主要用途。

功能包库 Fsharp.PowerPack

功能包(power pack)中有大量没有包含在FSharp.Core.dll 中的功能,这既有空间问题的原因,还因为它是带有试验性质,有可能比FSharp.Core.dll 的进化更快。它提供的模块能很好地与 OCaml 相兼容,有额外的集合,额外的数学函数,异步工作流(在第十章讨论),支持用fslex 和 fsyacc 进行文本解析的函数。下面我们就讨论Microsoft.FSharp.Math,这个命名空间包含了几个与数学相关的模块:任意精度的整数和理由、向量、矩阵和复数。

Microsoft.FSharp.Math命名空间

设计 Microsoft.FSharp.Math命名空间,是 F# 保证 F# 库包括了一些基础构造定义的使用更加广泛,如图形、数学、科学、工程等应用。首先,我们先简单地看一下这个模块的组成,然后,再分别看详细的示例。

它包含任意精度数,这是其值没有上限的数,包括在模块 BigInt 和 BigNum 中。典型的用途是在搜索大质数的程序中,可能用于加密应用。

模块 Matrix、Vector、RowVector 和 Notations 都包含了与矩阵和向量相关的运算。矩阵(Matrices)是按行列排列的一组数字集合,构成矩形数组;向量(Vectors)是一列数字,像只有一列的矩阵,但是单独的类型。向量的量由大小和方向描述,因此,二维向量由二个座标确定,三维向量由三个座标确定,等等。因此,向量的矩阵表示,由一列数字组成,而行数取决于向量的维度。

有一个模块 Complex,用于处理复数。复数(complex numbers)是许多不规则图形类型的基础,因此,我们会演示如何使用 F# 的复数库去画最著名的不规则图形,芒德布罗集合(Mandelbrot set)。反复迭代下面的等式,可以产生芒德布罗集合:

Cn+1 =Cn^2 + c

在这个序列中的下一个数等于当前数的平方加上起初数。如果反复迭代这个等式,停留在复数 C(1, 1i) 和 C(-1, -1i) 之间,那么起初复数就是芒德布罗集合的成员。下面的 F# 代码就能实现:

#r
@"FSharp.PowerPack.dll"

open Microsoft.FSharp.Math

//openMicrosoft.FSharp.Math.Notation

let cMax = complex 1.0 1.0

let cMin = complex -1.0 -1.0

let iterations = 18

let isInMandelbrotSet c0 =

let rec check n c =

(n = iterations)

|| (cMin < c) && (c < cMax)&& check (n + 1) ((c * c) + c0)

check 0 c0

[

1、需要引用 Fsharp.PowerPack;

2、没有且不需要 Microsoft.FSharp.Math.Notation 命名空间;

3、把or 要改成 ||

]

函数 isInMandelbrotSet 测试一个复数是否在芒德布罗集合中,通过递归调用 check 函数,每次用新 c 值计算 ((c * c) c0),直到这个复数在常量cMax 和 cMin 之间,或者迭代的次数超出常量 iterations。如果已经到达迭代次数iterations,那么这个数就是集合中的一个成员,否则,就不是。

因为复数由两个数字组成,因此,它可以用二维平面表示。芒德布罗复数介于 C(1, 1i) 和 C(-1, -1i) 之间,因此,画的这个平面要有一点原点,即点 0, 0,在中心,它的轴向两个方向延伸至最大值 1.0 和最小值 -1.0,如图 7-1 右边的平面。然而,当它应用到计算机屏幕上的像素时,必须考虑到平面的原点在左[原文为右]上角,向右、向下扩展。因为这种平面是由像素组成的,它是离散值,通常由整数表示,范围在 0 到 1600 之间,如图 7-1 左边的平面。

图 7-1. 位图平面与复数平面

应用程序必须把位图平面的点映射到复数平面,这样,才可以知道一个像素是否是在这个复数平面中。

只用几行 F# 代码就能很容易实现这个映射:

open Microsoft.FSharp.Math

let scalingFactor = 1.0 / 200.0

let offset = -1.0

let mapPlane (x, y) =

let fx = ((float x) * scalingFactor) + offset

let fy = ((float y) * scalingFactor) + offset

complex fx fy

一旦完成,只需要遍历位图平面上的所有点,用mapPlane 函数把它们映射到复数平面;然后,需要用isInMandelbrotSet 函数测试这个复数是否在芒德布罗集合中;最后,再设置像素的颜色。

完整程序如下:

open System

open System.Drawing

open System.Windows.Forms

open Microsoft.FSharp.Math

let cMax = complex 1.0 1.0

let cMin = complex -1.0 -1.0

let iterations = 18

let isInMandelbrotSet c0 =

let rec check n c =

(n = iterations)

|| (cMin < c)

&& (c < cMax)

&& check (n + 1) ((c * c) + c0)

check 0 c0

let scalingFactor = 1.0 / 200.0

let offset = -1.0

let mapPlane (x, y) =

let fx = ((float x) * scalingFactor) + offset

let fy = ((float y) * scalingFactor) + offset

complex fx fy

let form =

let image =
new Bitmap(400, 400)

for x = 0
to image.Width - 1 do

for y = 0
to image.Height - 1 do

let isMember = isInMandelbrotSet ( mapPlane (x, y) )

if isMember
then

image.SetPixel(x,y, Color.Black)

let temp =
new Form()
in

temp.Paint.Add(fun e
->e.Graphics.DrawImage(image, 0, 0))

temp

[<STAThread>]

do
Application.Run(form)

[

1、需要引用 Fsharp.PowerPack、System.Drawing、System.Windows.Forms;

2、如果使用交互模式,把 do
Application.Run(form) 改成 form.ShowDialog()

]

图 7-2 是程序产生的芒德布罗集合图形。

图 7-2 芒德布罗集合

第七章 小结

在这一章我们讨论了许多基础知识,因为,F#库提供了多种不同功能。首先,我们浏览了 FSharp.Core.dll库中的Collections、Reflection、Math 模块;然后,看了一下FSharp.PowerPack.dll,提供了构建所有应用程序都需要使用的优秀函数。Seq 模块是任何实质性的 F# 程序所不能没有的。

接下来三章我们将讨论如何使用 F# 与各种 .NET API 一起完成常见的编程任务。首先,在第八章看一下实现用户界面,然后,在第九章关注数据存取,在第十章讨论分布式应用。

第七章 F# 库(五),码迷,mamicode.com

时间: 2024-10-22 22:46:33

第七章 F# 库(五)的相关文章

第七章 F# 库(三)

序列(Microsoft.FSharp.Collections.Seq)模块 Microsoft.FSharp.Collections.Seq 模块包含所有处理集合的模块,只要它支持 IEnumerable 接口, .NET 框架的 BCL 中的大多数集合都是的.这个模块之所以称为序列(Seq),是因为序列是IEnumerable 接口的别名,是对其简称,为了读.写更方便.给定类型定义时使用这个别名. 注意 FSLib 包含几个模块,用于处理不同类型的集合,包括Array(数组).Array2(

第七章 F# 库(四)

打印(Microsoft.FSharp.Text.Printf)模块 打印(Printf)模块提供了以类型案例的方式格式化字符串的函数,打印模块中函数的第一个参数是值的占位符,它返回的函数需要为占位符提供值:占位符用百分号加一个表示类型的字母组成,表 7-2 是完整的清单. 表 7-2 打印模块的占位符和标记 标记 描述 %b 布尔型,格式化为 "true" 或 "false" %s 字符串,格式化为它的非转义内容 %d, %i 任何基本整型(即,sbyte, by

C和指针第七章第五题

实现一个简化的printf函数,能够处理%d,%f,%s,%c等格式. /************************************************************************* > File Name: permutation.c > Created Time: 2014年06月17日 星期二 23时22分34秒 *********************************************************************

阅读《大型网站技术架构:核心原理与案例分析》第五、六、七章

题目:阅读<大型网站技术架构:核心原理与案例分析>第五.六.七章,结合<XXX需求征集系统>,分析如何增加相应的功能,提高系统的可用性和易用性,撰写一篇1500字左右的博客阐述你的观点 在这一节课上,我们学习了系统质量属性其中的可用性和易用性.那么质量属性是什么呢,质量属性是高于对系统功能(即对系统能力.服务和行为)的基本的要求的.系统质量属性讲重点放在了可用性.可修改性.性能.安全性.可测试性和易用性.从设计师方面,系统质量属性一般存在三个问题:(1)为属性提供的定义并不是可操作

第四次作业:读软件工程课本五点五、六、七章感想与疑问

第五点五章 这一部分,本来在上一次就已经有过一次浏览,不过也真的只是浏览而已,哈哈,因为介绍了很多模型,看完后,又忘了,现在说要记住这些东西,对于我来时真的不是件容易的事,也可能觉得现在还没有用到这些东西吧,我曾几何时也在课堂上听老师杜给我们说过一些关于软件过程模型的东西,当时隐约记得介绍了八种模型,而重点要掌握的是瀑布模型,快速原型模型,Rational统一过程rup,微软模型.其实我是想知道我们在以后做一个软件时,是不是用期中的一个模型就可以了啊?还是说可以多个模型一起用?但是多个模型一起用

C Primer Plus (第五版) 第七章 编程练习

第七章 C控制语句:分支和跳转 编程练习: 1.编写一个程序.该程序读取输入直到遇到#字符,然后报告读取的空格数目.读取的换行符数目以及读取的所有其他字符数目. #include <stdio.h> int main(void) { int a=0, b=0, c=0; char ch; while ((ch = getchar()) != '#') { if (ch != ' ' && ch != '\n') c++; else if (ch != ' ') b++; els

《深入理解计算机系统》第七章学习笔记(初稿)

第七章 链接 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或拷贝)到存储器并执行.链接可以执行于编译时,也就是在源代码被翻译成机器代码时:也可以执行于加载时,也就是在程序被加载器加载到存储器并执行时:甚至执行于运行时,由应用程序来执行.在早期的计算机系统中,链接是手动执行的.在现代系统中,链接是由叫链接器的自动执行的. 理解链接器将帮助构造大型程序 理解链接器将帮助避免一些危险的编程错误 理解链接器将帮助语言的作用域规则是如何实现的 理解链接器将帮助其他重要

【转】第七章、Linux 文件与目录管理

原文网址:http://vbird.dic.ksu.edu.tw/linux_basic/0220filemanager.php 第七章.Linux 文件与目录管理 最近升级日期:2009/08/26 在第六章我们认识了Linux系统下的文件权限概念以及目录的配置说明. 在这个章节当中,我们就直接来进一步的操作与管理文件与目录吧!包括在不同的目录间变换. 创建与删除目录.创建与删除文件,还有寻找文件.查阅文件内容等等, 都会在这个章节作个简单的介绍啊! 1. 目录与路径 1.1 相对路径与绝对路

鸟哥的Linux私房菜_基础版_学习笔记3:第七章 Linux文件与目录管理

第七章 Linux文件与目录管理 7.1目录与路径: 7.1.1相对路径与绝对路径: 绝对路径:路径的写法『一定由根目录 / 写起』,例如: /usr/share/doc 这个目录. 相对路径:路径的写法『不是由 / 写起』,例如由 /usr/share/doc 要到 /usr/share/man 底下时,可以写成:『cd ../man』这就是相对路径的写法啦!相对路径意指『相对於目前工作目录的路径!』 7.1.2目录的相关操作: . 代表此层目录 .. 代表上一层目录 - 代表前一个工作目录