前面介绍了.Net下的基本类型,但对于F#,还有些类型是非常重要的。
Unit
Unit代表着什么都不是,它的意思有点像C#的void,但是void不是类型,而Unit是个类型。在F#里可以定义一个Unit类型的值。因为F#里的方法必须有返回值,所以当我们的某个方法不需要返回任何结果的时候,就可以在方法的最后写上句 () 也就是返回一个Unit类型。
let x = ();;
val x : unit = ()
在F#里提供了个ignore方法,它的作用是包装任意一个方法,使之返回Unit类型。
let f x = x * x
let y = ignore(f 10);;
val f : int -> int
val y : unit = ()
Tuple 元组
Tuple读作two-pull,元组是一组有序的数据。
let x = (0, 1, –1);;
val x : int * int * int = (0, 1, -1)
let x = (0, (), "1", ("ab", 'c'));;
val x : int * unit * string * (string * char) = (0, null, "1", ("ab", 'c'))
当一个元组只有2个元素的时候,F#里有2个特殊的变量可以分别代表第一个元素以及第二个元素:
let t = (0, 1)
printfn "fst is %d, snd is %d" (fst t) (snd t);;
fst is 0, snd is 1
val t : int * int = (0, 1)
当元组的元素不止2个的时候,你可以这样:
> let t = (1, 2, 3)
let x, y, z = t
printfn "x is %d, y is %d, z is %d" x y z;;
x is 1, y is 2, z is 3
val t : int * int * int = (1, 2, 3)
val z : int = 3
val y : int = 2
val x : int = 1
现在我们来用Tuple来实现下add方法:
> let add (x, y) = x + y;;
val add : int * int -> int
> add (1, 2);;
val it : int = 3
现在看起来有点点像C#里的方法,是吧:)
另外:
(1) = 1;;
val it : bool = true
List
列表的定义:
let lists = [“a”; "b”; "c”]
let emptyList = [];;
val lists : string list = ["a"; "b"; "c"]
val emptyList : 'a list
列表的常用一些操作:
双冒号操作(::),添加一个元素到列表的表头。
let lists = [1; 2; 3]
let other = 0 :: lists;;
val list : int list = [1; 2; 3]
val other : int list = [0; 1; 2; 3]
连接操作(&),将2个list合并。
let x = [1; 3; 5; 7; 9]
let y = [2; 4; 6; 8; 10]
let z = x @ y;;
val x : int list = [1; 3; 5; 7; 9]
val y : int list = [2; 4; 6; 8; 10]
val z : int list = [1; 3; 5; 7; 9; 2; 4; 6; 8; 10]
List Range
不知道这个特性该怎么翻译,在F#里,对于顺序的整数列表,提供了一种简便的定义方式。
let x = [0 .. 10];; // 定义一个从0到10的列表
val x : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
let x = [0 .. 10 .. 50];; // 定义一个从0 到50,间隔为10的列表
val x : int list = [0; 10; 20; 30; 40; 50]
let x = [0 .. 10 .. 49];; // 定义一个从0 到 49, 间隔为10的列表
val x : int list = [0; 10; 20; 30; 40]
let x = [10 .. –1 .. 0];; // 定义一个从10 到 0, 间隔为-1的列表
val x : int list = [10; 9; 8; 7; 6; 5; 4; 3; 2; 1; 0]
另外还可以通过表达式来定义列表:
let list x =
[
yield x - 1
yield x
yield x + 1
]
list 3;;
val list : int -> int list
val it : int list = [2; 3; 4]
let list x =
[
for n in 0 .. x do
if n % 3 = 0 then
yield n
];;
list 15;;
val it : int list = [0; 3; 6; 9; 12; 15]
列表相关函数
函数 | 类型 | 描述 |
List.length | 'a List –> int | 返回List的长度 |
List.head | 'a List –> 'a | 返回列表的第一元素 |
List.tail | 'a List –> 'a List | 返回列表除第一个元素外的元素列表 |
List.exists | ('a –> bool) –> 'a List –> bool | 返回是否存在满足条件的元素 |
List.rev | 'a List –> 'a List | 反转列表后返回 |
List.tryfind | ('a –> bool) –> 'a List –> 'a opion | 返回some(‘a)当满足的条件的时候,否则返回none |
List.zip | 'a List –> 'b List –> ('a * ‘b) List | 将2个相同长度的list,将其元素组成元祖,然后返回List |
List.filter | ('a –> bool) –> 'a List –> 'a List | 将list中满足条件的元素以list的形式返回 |
List.partition | ('a –> bool) –> 'a List –> ('a List * 'a List) | 将list按照条件分为2个list,一个是满足条件的,一个是不满足的 |
还有些更Powerful的函数:
List.map
通过一个'a –> 'b的函数,将列表转换成另一个列表。
('a –> 'b) –> 'a List –> 'b List
借用下书中的图
比如,当有一个字符列表需要转换成byte列表,在C#里一般会写成:
List<byte> bytes = new List<byte>();
foreach(char c in chars) bytes.Add((byte)c);
而在F#里可以更优雅的写成:
> List.map (fun x -> byte x) ['a'; 'b'; 'c'];;
val it : byte list = [97uy; 98uy; 99uy]
List.reduce
该方法接受一个'a –> 'a –> 'a的方法,依次遍历列表,将元素作为参数传递给该方法,并将方法的结果作为参数,以及下一个元素再次传给该方法,直至列表结束。
比如,计算[1 .. 10]所有元素的和。
List.reduce (fun x y –> x + y) [1 .. 10];;
val it : int = 55
计算过程可以理解为:
1.let x = 1 + 2
2.let x = x + 3
3.let x = x + 4
…
9.let x = x + 10
10. x
Btw,上面的代码还可以进一步简化,我们定义的 fun x y –> x + y其实等同于操作符'+’,所以我们可以写为:List.reduce (+) [1 .. 10];;,怎么样,是不是很简单呢
List.fold
List.reduce方法返回结果的类型同List的类型相同,可是当我们需要的结果类型跟List不同的时候,就需要List.fold方法了。比如我们要计算['a’; 'b’; 'c’]的ASCII码值的和。
List.fold (fun x y –> x + (int y)) 0 [‘a’; 'b’; 'c’];;
val it : int = 294
List.reduceBack & List.foldBack
这两个方法在《Programming F#》这本书里的描述是:"List.reduce and List.fold process the list in a left-to-right order. There are alternative functions List.reduceBack and List.foldBack for processing lists in right-to-left order. "。这段话,我理解了很久,做了一些测试,才理解了它的处理过程。
List.reduceBack (-) [1 .. 5];;
最开始,我以为它的结果应该是
let x = 5 – 4
let x = x - 3
let x = x – 2
let x = x – 1
x // –5
可是结果是:3。如果要得到3这个结果,我又猜测这个方法的执行方式是:
let x = 2 – 1
let x = 3 – x
let x = 4 – x
let x = 5 – x
x // 3
这回结果是对了,我又接着测试了下面的方法:
List.reduceBack (+) ["a"; "b"; "c"; "d"; "e"];;
如果按照上面的处理过程,结果应该是:“edcba”,可是结果是“abcde” ,看来前面又错了。这样的话,正确的执行过程应该是:
let x = 4 – 5 // -1
let x = 3 – x // 4
let x = 2 – x // -2
let x = 1 – x // 3
x // 3
现在来写个方法,把参数打印出来看看:
let add x y =
printfn "%d, %d" x y
x + y
List.reduceBack add [1 .. 5];;
4, 5
3, 9
2, 12
1, 14
这回真的猜对了。其实是我笨,猜老半天,就没想到直接打出来看看,呵呵。
唉,这个话题好长,写了3天了,还有个Option类型,实在不想写了。把这篇拆成2篇写好了。
收工,睡觉~~