前面,我们介绍了.NET平台提供的基本类型,但这些单独的类型不足以建立有效的程序。在F#库包括几个核心类型,让你组织,操作和处理数据,下面列出了一套在你的F#应用程序中需要用到的基本类型:
Signature |
Name |
Description |
Example |
unit |
Unit |
A unit value |
() |
int, float |
Concrete type |
A concrete type |
42, 3.14 |
'a, 'b |
Generic type |
A generic (free) type |
|
'a -> 'b |
Function type |
A function returning a value |
fun x -> x + 1 |
'a * 'b |
Tuple type |
An ordered collection of values |
(1, 2), ("eggs", "ham") |
'a list |
List type |
A list of values |
[ 1; 2; 3], [1 .. 3] |
'a option |
Option type |
An optional value |
Some(3), None |
Unit类型
Unit代表着一个值标志函数或者表达式没有任何返回结果,跟C#的void类似,unit可以被看作是无效的具体体现,在代码中是通过();来表示:
> let x = ();;
val x : unit
> ();;
val it : unit = ()
if语句没有匹配的else必须返回unit。另外,在F#,每个函数必须返回一个值,因此,如果该函数从概念上不返回任何东西,如printf,那么它应返回unit。 如果你想返回unit,ignore函数可以忽略一个函数的返回值,它通常用于在调用一个函数时你要忽略它的返回值:
> let square x = x * x;;
val square : int -> int
> ignore (square 4);;
val it : unit = ()
Tuple类型
元组是数据的有序集合,提供了一个简单的方法将具有共同特性的数据组合在一起,例如,元组可以被用来跟踪一个计算的中间结果。F#元组使用底层System.Tuple <_>类型,但在实践中,可以直接使用Tuple<_>。
要创建一个元组,使用逗号符将一组数据分隔来开,并且可以选择将它们置于括号里面,元组类型是描述一个元组的元素类型的列表,以星号隔开。在下面的例子,dinner是一个元组的实例,而string*string是元组的类型:
> let dinner = ("green eggs", "ham");;
val dinner : string * string = ("green eggs", "ham")
元组可以包含任何类型的值,事实上,你甚至可以有一个元组包含其他元组。下面的代码片断定义了两个元组,第一,命名为zeros,包含了一组0在各个类型中的表现形式;第二,nested,定义了一个嵌套的元组:
> let zeros = (0, 0L, 0I, 0.0);;
val zeros : int * int64 * bigint * float = (0, 0L, 0I, 0.0)
> let nested = (1, (2.0, 3M), (4L, "5", '6'));;
val nested : int * (float * decimal) * (int64 * string * char) = ...
要提取的两元素元组值,你可以使用fst和snd函数,fst返回元组的第一个值,snd返回元组的第二个值:
> let nameTuple = ("John", "Smith");;
val nameTuple : string * string = ("John", "Smith")
> fst nameTuple;;
val it : string = "John"
> snd nameTuple;;
val it : string = "Smith"
或者,你可以像在Erlang语言中的模式匹配来提取元组的值。如果你利用let,用逗号分隔多个标示符(元组有多少元素,就应该有多少标识符)。这样,每个标识符对应元组的每一个元素。下面的示例创建一个元组snacks,元组的值提取到指定新标识符x,y,z中:
> let snacks = ("Soda", "Cookies", "Candy");;
val snacks : string * string * string = ("Soda", "Cookies", "Candy")
> let x, y, z = snacks;;
val z : string = "Soda"
val y : string = "Cookies"
val x : string = "Candy"
> y, z;;
val it : string * string = ("Cookies", "Candy")
你会得到一个编译错误如果您尝试在一个元组中提取过多或过少值:
> let x, y = snacks;;
let x, y = snacks;;
-----------^^^^^^
stdin(8,12): error FS0001: Type mismatch. Expecting a
string * string
but given a
string * string * string.
The tuples have differing lengths of 2 and 3.
但是,如果我只需要提取snacks中的第三个值,而又不想新建另外两个标识符,该怎么提取呢?可以使用下面的办法:
> let _,_,x = snacks;;
val x : string = "Candy"
> x;;
val it : string = "Candy"
>
元组也可以像任何值样可以作为函数的参数,在下面的例子,函数tupledAdd两个参数x和y,并且x和y是来自于元组的形式,注意在函数add和tupledAdd中两者签名的不同:
> let add x y = x + y;;
val add : int -> int -> int
> let tupledAdd(x, y) = x + y;;
val tupledAdd : int * int -> int
> add 3 7;;
val it : int = 10
> tupledAdd(3, 7);;
val it : int = 10
Lists类型
元组组成的值只是一个单一的实体值,而List允许你把数据组成一个有序的链接,这个可以使用聚合操作来处理列表中的大量元素。
列表最简单的定义方式是用分号来分隔每个值,并且所有的值都封闭在一个中括号中;没有任何值的空列表,使用[]来代替:
let vowels = ['a'; 'e'; 'i'; 'o'; 'u']
let emptyList = [];;
val vowels : char list = ['a'; 'e'; 'i'; 'o'; 'u']
val emptyList : 'a list = []
在上面的例子中,空列表有一个'a list类型,是因为它可以是任何值,并且类型判断系统也无法指定一个特殊的类型
有别于其他类型的语言列表,F#列表对于你如何访问和操作它们在相当大的限制性,事实上,在一个list中只有两个操作可以执行。第一个操作是con,由::来表示,其作用是在一个列表头加入一个新的元素,如下面的代码:
> let vowels = ['a'; 'e'; 'i'; 'o'; 'u'];;
val vowels : char list = ['a'; 'e'; 'i'; 'o'; 'u']
> let sometimes = 'y' :: vowels;;
val sometimes : char list = ['y'; 'a'; 'e'; 'i'; 'o'; 'u']
第二个列表操作,就是追加,使用@操作符,作用是把两个list组合成一个新的列表,如下面的代码:
> let odds = [1; 3; 5; 7; 9]
- let evens = [2; 4; 6; 8; 10];;
val odds : int list = [1; 3; 5; 7; 9]
val evens : int list = [2; 4; 6; 8; 10]
> odds @ evens;;
val it : int list = [1; 3; 5; 7; 9; 2; 4; 6; 8; 10]
> odds;;
val it : int list = [1; 3; 5; 7; 9]
> evens;;
val it : int list = [2; 4; 6; 8; 10]
1、List ranges
声明作为一个分号分隔的列表很快变得冗长,尤其是很大的元素列表。要声明一个有序的数字值的列表,可以使用list range语法,表达式的第一个指定区间的下限,第二个指定上界,其结果则是列表的值从指定的下限并且以增量为1的形式一直到指定的上限:
> let x = [1 .. 10];;
val x : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
如果指定一个增量值,那么列表内的每个值是以指定的增量来增长的,请注意,增量值可以是负数:
> let tens = [0 .. 10 .. 50]
- let countDown = [5L .. -1L .. 0L];;
val tens : int list = [0; 10; 20; 30; 40; 50]
val countDown : int64 list = [5L; 4L; 3L; 2L; 1L; 0L]
2、List comprehensions
用于创建列表的最有效的方法是使用list comprehensions,它是一个有趣的语法,可是使你利用内联的F#代码生成列表。简单的来说,
列表可以理解中括号[]里面包含了一些代码,[]里面的代码将被执行,并且通过关键字yield返回来的值作为列表元组,组成一个列表:
> let numbersNear x =
- [
- yield x-1
- yield x
- yield x+1
- ];;
val numbersNear : int -> int list
> numbersNear 3;;
val it : int list = [2; 3; 4]
几乎所有的F#代码都可以存在于list comprehensions中,甚至是函数和for循序:
> let x =
- [
- let negate x = -x
- for i in 1 .. 10 do
- if i%2 = 0 then
- yield negate i
- else
- yield i
- ];;
val x : int list = [1; -2; 3; -4; 5; -6; 7; -8; 9; -10]
当列表内涵内的代码使用for循环时,可以使用->代替do yield来简化代码,下面的两个代码片段作用是相同的:
> let multiplesOf x = [ for i in 1 .. 10 do yield x * i ]
- let multiplesOf2 x = [ for i in 1 .. 10 -> x * i ];;
val multiplesOf : int -> int list
val multiplesOf2 : int -> int list
> multiplesOf2 3;;
val it : int list = [3; 6; 9; 12; 15; 18; 21; 24; 27; 30]
> multiplesOf 3;;
val it : int list = [3; 6; 9; 12; 15; 18; 21; 24; 27; 30]
使用列表内涵语法,使您可以快速而简洁地生成数据列表,然后在你的代码里进行处理。下面的代码显示了如何使用列表内涵生成小于一个给定整数内的所有素数,代码中通过循环从1到给定的最大数,然后使用列表内涵通过表达式条件来生成元素,它会判断生成的列表里面的元素,如果只有两个,则通过yield关键字返回,因为它是一个素数:
> let primesUnder max =
- [
- for n in 1 .. max do
- let factorsOfN =
- [
- for i in 1 .. n do
- if n % i = 0 then
- yield i
- ]
- if List.length factorsOfN = 2 then
- yield n
- ];;
val primesUnder : int -> int list
> primesUnder 50;;
val it : int list = [2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47]
3、List module functions
在F#库中List模块包含有很多方法来帮助你处理列表,下列表格中列出的方法将是你在F#中经常使用到的在列表中:
Function |
type |
Description |
List.length |
'a list > int |
Returns the length of a list. |
List.head |
'a list -> 'a |
Returns the first element in a list. |
List.tail |
'a list -> 'a list |
Returns the given list without the first element. |
List.exists |
('a -> bool) -> 'a list -> bool |
Returns whether or not an element in the list satisfies the search function. |
List.rev |
'a list -> 'a list |
Reverses the elements in a list. |
List.tryfind |
('a > bool) > 'a list > 'a option |
Returns Some(x) where x is the first element for which the given function returns true. Otherwise returns None. |
List.zip |
'a list -> 'b list -> ('a * 'b) list |
Given two lists with the same length, returns a joined list of tuples. |
List.filter |
('a -> bool) -> 'a list -> 'a list |
Returns a list with only the elements for which the given function returned true. |
List.partition |
('a -> bool) -> 'a list -> ('a list *'a list) |
Given a predicate function and a list returns two new lists, the first where the function returned true, the second where the function returned false. |
> let isMultipleOf5 x = (x % 5 = 0)
- let multOf5, nonMultOf5 =
- List.partition isMultipleOf5 [1 .. 15];;
val isMultipleOf5 : int -> bool
val nonMultOf5 : int list = [1; 2; 3; 4; 6; 7; 8; 9; 11; 12; 13; 14]
val multOf5 : int list = [5; 10; 15]