列表推导(List Comprehensions) 列表推导的概念源于数学,它使得创建和转换集合的操作变得简单。在F#中可以使用这样的推导语法直接创建列表,序列(sequence)和数组(序列和数组将在后面介绍)。要了解这个概念的更多内容可以查看:
List_comprehension。
最简单的情况是指定列表的范围,如:

Code
let numericList = [0 .. 9]
let charList = ['A' .. 'Z']
这两个列表的类型分别是int list和char list,范围分别是从0到9和从’A’到’Z’。
更复杂的情况是指定一个步长:

Code
let multipleOfThree = [0 .. 3 .. 30]
let revNumericList = [9.. -1 .. 0]
第一个列表的值是0到30间所有3的倍数,第二个列表的元素则包含了从9递减至0。
我们还可以通过对一个列表进行循环操作得到另一个列表。例如:

Code
let squares = [for x in
1 .. 10
-> x * x]
通过for进行循环,squares列表的元素是1到10间的整数的平方。
此外还可以为循环添加when子句对元素进行过滤,只有when子句的值为true时才对其进行运算:

Code
let evens = [for x in
1 .. 10
when x % 2 = 0
-> x]
evens的元素为[2; 4; 6; 8; 10]。
控制流程(Control Flow)F#拥有强的控制流程概念,这与很多纯函数式编程语言不同,在这些语言中表达式可以以任何顺序进行求值。看下面的例子:

Code
let absoluteValue x =
if x < 0
then
-x
elif x = 0
then
0
else
x
if, elif, then, else组成的结构我们应当很熟悉,在F#中该结构是一个表达式,也就是说它需要返回一个值。而且每个分支返回的值应当具有相同的类型,否则就会有编译错误。如果确实要返回多个类型的值,在值前加box关键字,就像前面创建列表时那样,这样表达式的返回类型为obj。
类型与类型推导(Types and Type Inference) F#是一种
强类型的语言,传给函数的值必须是指定的类型。如果函数接受string类型的参数,就不能传给它int类型的值。一种语言处理其中值的类型的方式称为语言的类型系统。F#的类型系统与一般语言不同,包括函数在内,所有的值都具有自己的类型。
通常情况下,我们不需要显式地声明类型,编译器会尝试从值的文字值或调用的函数返回类型来判断其类型,这个过程称为
类型推导。可在编译时使用-i开关来显示所有的推导类型,在VS中我们则可以使用工具提示来查看标识符的类型。先看下面值的类型推导情况:

Code
let strValue = "String Value"
let intValue = 12
在fsi中可看到它们的信息是:

Code
val strValue : string
val intValue : int
可以理解,编译器跟据赋给标识符的文字值来推导其类型。再看看下面函数的情况:

Code
let makeMessage x = (string_of_bool x) + " is a boolean value"
let half x = x / 2
在fsi中可看到它们的信息是:

Code
val makeMessage : bool
->
string
val half : int
->
int
有意思的是,函数名前面也有个val,这表明函数也是值,后面的bool -> string是什么意思呢?它表明函数接受bool类型参数,返回string类型的值,注意x作为string_of_bool的参数,所以x必须为bool类型,返回值是两个字符串相加的值,故返回值也是string类型。对于half函数,单从定义不能确定x类型,此时编译器采用默认的类型int。再看看稍微复杂点的情况:

Code
let div1 x y = x / y
let div2 (x, y) = x / y
这两个函数的信息是:

Code
val div1 : int
->
int
->
int
val div2 : int * int
->
int
div1函数可接受部分参数(可柯里化),而div2则必须同时传入两个int类型的值。考虑下面的函数:

Code
let doNothing x = x
其信息为:

Code
val doNothing : 'a -> 'a
a’ -> a’表示函数接受任意类型,并返回与其相同类型的值。以’打头的类型表示
可变类型(variable type),编译器虽然不能确定类型的参数,却能确定返回值类型必须与参数类型相同,类型系统的这种特性称为
类型参数化,通过它编译器也能发现更多的类型错误。可变类型或类型参数化的概念,类似于.NET 2.0的泛型,如果F#基于支持泛型的CLI,那么它会充分利用泛型的优势。另外,F#的创建者Don Syme,正是CLR中泛型的设计者和实现者。
F#的类型推导固然强大,但它显然不能揣测出开发人员所有的心思来,如果有特殊需求该怎么办呢?看下面的例子:
let doNothingToFloat (x : float32) = xfloat32即System.Single,这里我们手动指定了x的类型,这个有时称为类型标注(type annotation)。如果要在F#中使用其它.NET语言编写的类库,或者与非托管的类库进行互操作,它会很有用。
小结 接
上一篇,本文继续介绍F#中的函数式编程范式,主要包含了操作符、列表、列表推导、类型推导、类型标注等概念。类型推导又称隐式类型,通常是——但不限于——函数式编程语言的特性,比如C# 3.0和VB.NET 9.0都提供了一定的支持,它使很多编程任务变得更为简单。
参考:
《Foundations of F#》 by Robert Pickering
《
F# Specs》