第七章 F# 库(一)
虽然 F# 可以使用 .NET BCL中的所有类,但它自己也提供了一组库。
[
现代汉语中使用单字名词的已经很少了,因此,libraries有时译成库,有时译成库函数。严格来讲译成库函数是不准确的,因为,库中肯定不止包含函数。
]
F# 库分成两部分,一个是 FSharp.Core.dll,也称为 F#核心库(F# core library)或本机 F#库(native F# library);另一个是 FSharp.PowerPack.dll,有时直接就称 power pack。F#核心库包含了 F# 编译器运行所需要的一切。比如,它包含元组(Tuple)类,用于处理元组;power pack为 F# 核心库补充了其他有用的功能。把库分成两部分,有两个主要的原因:第一,努力使核心库保持尽可能小,这样,应用程序的开发人员努力使对 FSharp.Core.dll的依赖占据小的空间,而不致引起大的问题;第二,也可能更重要,使 FSharp.Core.dll保持尽可能稳定,这样,只随新版本的编译器而改变,而 F# 的团队发布新版本的 FSharp.PowerPack.dll,这里会有更多有趣的新的功能。
本章的目标不是成为介绍 F# 库中类型和函数的完整文档,它只是一个概览,知道模块能名做什么,特别关注那些在 BCL 中没有的功能。F# 的在线文档(http://msdn.microsoft.com/fsharp)可以找到每一个函数的详细文档。
本机 F# 库 FSharp.Core.dll
本机 F# 库包含了编译器能够运行的所有类,比如,F#列表编译的类型定义。我们将讨论下列几个模块:
Microsoft.FSharp.Core.Operators:这个模块中包含了数学运算符的函数。
Microsoft.FSharp.Reflection:这个模块中包含的函数,用于补充 .NET框架的反映类型,为了获得 F# 类型和值的更精确视图。
Microsoft.FSharp.Collections.Seq:这个模块中包含的函数用于处理所有支持 IEnumerable接口的类型。
Microsoft.FSharp.Text.Printf:这个是用于字符串的模块。
Microsoft.FSharp.Control.Event:这个是用于处理 F#事件的模块。
运算符(Microsoft.FSharp.Core.Operators)模块
F# 中,运算符是由库定义的,而不是语言内置的。这个模块包含了一些语言的运算符;还包含了一些有用的运算符,比如函数,这些也正是我们将要讨论的。这个模块默认被引用,就是说,用户可以使用其中的函数,而不要前缀。我们将专门讨论下面几个函数类型:
算术运算符(Arithmetic operators):基本的用于算术运算的运算符,比如加法和减法。
浮点数函数(Floating-point arithmetic functions):高级算术运算,包括对数和三角函数。
可变的整数函数(Mutable integer functions):用于处理可变整数的函数。
元组函数(Tuple functions)用于处理元组的函数。
转换函数(Conversion functions):用于在基本类型(primitive types)之间的转换,比如字符串、浮点数和整数。
算术运算符(Arithmetic Operators)
正如我们在第二章中已经讨论的,F#的运算符能够由程序员定义,因此,所有的算术运算符都是在 Operators 模块中定义的,而不是语言所内置的。因此,我们日常使用 F# 编程的运算符大多数都是在 Operators模块中定义的。我们假设这些运算符,比如 + 和 -,几乎不需要解释,因为它们的用法很简单:
let x1 = 1 + 1
let x2 = 1 – 1
默认情况下,F# 的运算符是不检查的,这样,如果一个值太大,那么有可能会绕回。因此,值有可能会变得很小,但并不引发错误。如果我们打算检查值溢出时,触发异常的运算符,需要引用 Microsoft.FSharp.Core.Operators.Checked:
open Microsoft.FSharp.Core.Operators.Checked
let x = System.Int32.MaxValue + 1
现在,运行上面的示例就会出错,而如果没有引用 Microsoft.FSharp.Core.Operators.Checked,那么,x的值会直接绕回到 -2147483648。
F# 的相等运算符与其大多数算术运算符相比,有一点微妙,这是因为 F#的相等是结构相等(structural equality),即对对象的内容进行比较,检查组成对象的每一项是否相等;它与引用相等(referential equality)相对,即确定两个标识符是否绑定到同一个对象,或者同一段物理内存空间。引用相等的检查通过执行 obj.ReferenceEquals方法;结构相等的运行符是等号(=),结构不等的运算符是 <>。下面的示例有所演示。记录 robert1和 robert2相等,因为,它们虽然是不同对象,但它们的内容相同;而 robert1和 robert3 不等,因为它们的内容不同。
type person = { name : string ; favoriteColor : string }
let robert1 = { name = "Robert" ; favoriteColor = "Red" }
let robert2 = { name = "Robert" ; favoriteColor = "Red" }
let robert3 = { name = "Robert" ; favoriteColor = "Green" }
printfn "(robert1 = robert2): %b" (robert1 = robert2)
printfn "(robert1 <> robert3): %b" (robert1 <> robert3)
代码的运行结果如下:
(robert1 = robert2): true
(robert1 <> robert3): true
结构比较(Structural comparison)使用大于(>)和小于(<)运算符实现,因此,它们也可以用来比较 F#的记录类型。下面是演示:
type person = { name : string ; favoriteColor : string }
let robert2 = { name = "Robert" ; favoriteColor = "Red" }
let robert3 = { name = "Robert" ; favoriteColor = "Green" }
printfn "(robert2 > robert3): %b" (robert2 > robert3)
代码的运行结果如下:
(robert2 > robert3): true
如果需要确定两个对象是否物理上相等,可以使用 PhysicalEquality函数,在 LanguagePrimitives 模块中,如下面的示例:
type person = { name : string ; favoriteColor : string }
let robert1 = { name = "Robert" ; favoriteColor = "Red" }
let robert2 = { name = "Robert" ; favoriteColor = "Red" }
printfn "(LanguagePrimitives.PhysicalEquality robert1 robert2): %b"
(LanguagePrimitives.PhysicalEquality robert1 robert2)
浮点数函数(Floating-point arithmetic functions)
Operators 模块还提供了大量的函数(见表 7-1),专门用于浮点数的运算,下面的示例是一些应用:
printfn "(sqrt 16.0): %f" (sqrt 16.0)
printfn "(log 160.0): %f" (log 160.0)
printfn "(cos 1.6): %f" (cos 1.6)
代码的运行结果如下:
(sqrt 16.0): 4.000000
(log 160.0): 5.075174
(cos 1.6): -0.029200
表 7-1 浮点数函数
函数 | 描述 |
abs | 返回参数的绝对值 |
acos | 返回参数的反余弦(arccosine),参数以弧度表示 |
asin | 返回参数的反正弦(arcsine),参数以弧度表示 |
atan | 返回参数的反正切(arctangent),参数以弧度表示 |
atan2 | 返回两个参数的反正切(arctangent),参数都以弧度表示 |
ceil | 返回下一个最大的整数值,如果需要,值会舍入;返回值仍然是浮点数 |
floor | 返回下一个最小的整数值,如果需要,值会舍入;返回值仍然是浮点数 |
exp | 以自然常数 e 为底的指数函数 |
infinity | 返回表示无穷大的浮点数 |
log | 返回浮点数的自然对数 |
log10 | 返回浮点数的以 10 为底对数 |
nan | 返回表示“不是数字”的浮点数 |
sqrt | 返回平方根 |
cos | 返回参数的余弦,参数以弧度表示 |
cosh | 返回参数的双曲余弦,参数以弧度表示 |
sin | 返回参数的正弦,参数以弧度表示 |
sinh | 返回参数的双曲正弦,参数以弧度表示 |
tan | 返回参数的正切,参数以弧度表示 |
tanh | 返回参数的双曲正切,参数以弧度表示 |
truncate | 返回参数的整数部分;返回值仍然是浮点数 |
float | 返回整数的浮点(float)数 |
float32 | 返回整数的浮点(float32)数 |
元组函数(Tuple Functions)
Operators 模块中还有两个有用的函数用于处理元组,函数 fst和 snd 把有两项的元组拆分并取出。下面的示例演示了其用法:
printfn "(fst (1, 2)): %i" (fst (1, 2))
printfn "(snd (1, 2)): %i" (snd (1, 2))
代码的运行结果如下:
(fst (1, 2)): 1
(snd (1, 2)): 2
转换函数(Conversion Functions)
operator 模块还提供了大量的重载函数(overload function),用于在基本类型(primitive type)之间进行转换。例如,函数 float被重载,把字符串或整数转换成浮点数 System.Double。下面的示例演示了把一个枚举转换成整数,再把它转回枚举。把枚举转换成整数很简单,只要用 int函数就行了;把整数转回枚举有点复杂了,需要使用 enum 函数,还必须提供类型类型注解,使编译器能够知道所整数转换成哪一种枚举。可以看到,在下面的示例中,为标识符 dayEnum添加的注解 DayOfWeek:
open System
let dayInt = int DateTime.Now.DayOfWeek
let (dayEnum : DayOfWeek) = enum dayInt
printfn "%i" dayInt
printfn "%A" dayEnum
代码的运行结果如下:
0
Sunday
逻辑与和或运算符
需要利用枚举执行的另一组任务,一是用逻辑或组合枚举,二是用逻辑与测试枚举。枚举类型用 System.Flags特征标记,支持使用 &&& 和 |||运算符直接执行这两个运算。例如,可以使用 ||| 运算符组合几个枚举值;以 v1 &&& v2 <> enum 0形式使用 &&& 运算符,测试值是否是枚举的一部分:
open System.Windows.Forms
let anchor = AnchorStyles.Left ||| AnchorStyles.Left
printfn "test AnchorStyles.Left: %b"
(anchor &&& AnchorStyles.Left <> enum 0)
printfn "test AnchorStyles.Right: %b"
(anchor &&& AnchorStyles.Right <> enum 0)