F#学习笔记核心类型(一)

前面介绍了.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

借用下书中的图

image

比如,当有一个字符列表需要转换成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篇写好了。

收工,睡觉~~

原文地址:https://www.cnblogs.com/FMax/p/1739655.html