F#小记——2. 基本数据类型

F#基本数据类型:

数字:

C#一样,F#的数字类型和.net中的类型是一一对应的,只不过为了方便使用,又对其定义了一套别名而已,可以通过后缀区分其数据类型。

类型

后缀

.NET 类型

byte

uy

System.Byte

sbyte

y

System.SByte

int16

s

System.Int16

uint16

us

System.UInt16

int,int32

System.Int32

uint, uint32

u

System.UInt32

int64

L

System.Int64

uint64

UL

System.UInt64

float

System.Double

float32

f

System.Float

decimal

M

System.Decimal

另外,也可以通过前缀0x0o0b来标识十六进制、八进制和二进制数据。

> let hex = 0xFCAF;;

val hex : int = 64687

> let oct = 0o7771L;;

val oct : int64 = 4089L

> let bin = 0b00101010y;;

val bin : sbyte = 42y

> (hex, oct, bin);;

val it : int * int64 * sbyte = (64687, 4089, 42)

运算:

数学运算是函数式编程的强项,F#提供了非常丰富的运算符

  • 基本运算:     +-*/%**
  • 位运算:         &&&|||^^^<<<>>>
  • 布尔运算:     &&||not
  • 比较:              <<=>>==<>compare
  • 数学函数:     abs ceil exp floor sign log log10 cos sin tan pown

字符和字符串:

F#中的字符和字符串和C#类似,就不多做介绍了。

Unit

Unit类型主要用于函数的返回值,用来表示函数不需要返回任何数据,类似于C#中的voidUnit类型可以用()来表示:

> let x = ();;

val x : unit

 

> ();;

val it : unit = ()

如同C语言里不使用返回值不是void类型的函数的返回值会得到一条pclint告警一样,使用了返回值不是unit的函数也会得到一条告警:

> let square x = x * x;;

val square : int -> int

 

> square 4;;

val it : int = 16

Warning 1   This expression should have type 'unit', but has type 'int'. Use 'ignore' to discard the result of the expression, or 'let' to bind the result to a name.

为了消除这个告警,我们可以使用系统内置的ignore函数。ignore函数返回一个unit类型,其功能如下:

let ignore x = ()

因此使用了ignore函数后,就改变了返回值为unit,自然就消除了这个告警。

> ignore (square 4);;

val it : unit = ()

但是,这样写不是很优雅,我们常常通过管道符函数‘|>’来写成如下形式

> square 4 |> ignore;;

这种方式让我想起来了在unixshell重定向时常用的“… > null 0”结构。

Tuples

Tuples两个或多个值的集合,基本形式如下:

> let dinner = ("green eggs", "ham");;

val dinner : string * string = ("green eggs", "ham")

 

> let zeros = (0, 0L, 0I, 0.0);;

val zeros : int * int64 * bigint * float = (0, 0L, 0I, 0.0)

其中,小括号是可选的,如下形式也合法,但不推荐。

> let dinner = "green eggs", "ham";;

获取Tuples中的元素值通常有如下两种方式:

1.       通过系统内置的fstsnd函数

> let a = fst (1, 2);;

val a : int = 1

 

> let b = snd (1, 2);;

val b : int = 2

2.       通过let绑定

> let (a, b) = (1, 2);;

val b : int = 2

val a : int = 1

可能有细心的朋友已经注意到了,系统只提供fstsnd函数,如果要对Tuples的第三个或以上元素该如何做呢?当然还是可以通过这两种方法啦:

1.       自定义third函数,然后通过函数获取

> let third (a, b, c) = c;; //由于这里不关心前两个元素,这个函数也可以写成let third (_, _, c) = c;;

val third : 'a * 'b * 'c -> 'c

 

> third (1, 2, 3);;

val it : int = 3

2.       通过let绑定的方式

> let (_, _, c) = (1, 2, 3);;

val c : int = 3

这两种方式都是异曲同工的,我个人更喜欢let绑定的方式,用起来更加简洁优雅。

Lists

ListsF#中内置的链表,基本形式如下:

> [];; //empty list

val it : 'a list = []

> [1; 2; 3];;

val it : int list = [1; 2; 3]

注意:List中元素是通过 ‘;’ 来分隔的,不要像C#那样用 ‘,’ 来分隔,一旦用了 ‘,’ ,得到的将是一个包含一个Tuples元素的List

> [1, 2, 3];;

val it : (int * int * int) list = [(1, 2, 3)]

另外,我们也可以通过推导的方式,写出步长和范围,让系统自动推导List中的其它元素。

> [1 .. 10];;

val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

> [1 .. 10 .. 50];;

val it : int list = [1; 11; 21; 31; 41]

:: 来连接List的头和尾

一个List有一个“头”(第一个元素)和一个“尾”(剩下的元素)。头是一个元素,而尾则是一个列表。例如,对于[1; 2; 3]来说,表头是整数1,而表尾是list[2; 3]

F#中,可以通过::运算符来连接头和尾,下面几种的列表写法是完全等价的:

[1; 2; 3]

1 :: [2; 3]

1 :: 2 :: [3]

1 :: 2 :: 3 :: []

这个运算符主要用于模式匹配,在讲模式匹配的时候再介绍其用途。

@ 来连接两个List

类似于字符串可以用 + 运算符来连接一样,系统也内置了一个 @ 运算符来连接两个List,生成一个新的List

> // Using the append operator

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]

 

yield来返回一个List

当函数返回值是一个List的时候,用上面的几种方式来构造List往往不是很方便,特别是在不确定元素个数的时候。因此,F#提供了一个关键字yield来方便我们构造List的返回值。

> // Simple list comprehensions

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]

使用方式基本上和C#中的yield差不多,简单而方便。

Option

C#中,当无法获取到预期对象的时候,我们往往用null来标示这个值为空。这样导致我们再使用它的时候往往需要判断对象是否为null,多加判断会导致代码可读性差,漏加判断常常导致程序的一些bugC/C++会导致更严重的数据访问异常)。

F#中,通过引进option类型来解决这一问题:option类型的数据的值有两种类型——Some('a) None,通过它们可以区分数据是否为空数据。

> // Using option to return a value (or not)

open System

let isInteger str =

    let successful, result = Int32.TryParse(str)

    if successful then Some(result)

else None;;

val isInteger : string -> int option

 

> isInteger "This is not an int";;

val it : int option = None

 

> isInteger "400";;

val it : int option = Some 400

要获取option中的元素值,可以通过Option.get函数来实现,当值为None的时候,Option.get会抛异常。如果要避免这个异常,可以通过Option.isSomeOption.isNone先判断一下。通过模式匹配可以更好的实现这一功能。

let printNumber str =

    match (isInteger str) with

    | Some(num) -> printfn "Number: %d" num

| None       -> printfn "Invalid Number"

实际上,option也无法避免空值的判断,但是F#通过option明确指出了哪些需要判断,哪些不需要判断,这样对我们的代码无疑是非常有帮助的。

其它数据类型:

除了上述这几种基本类型以外,还有几种类型:Recordstructclass,Discriminated Unions等,这些的使用方法相对复杂点,在后续讲述函数式编程的时候再详细讲。

 

原文地址:https://www.cnblogs.com/TianFang/p/1774013.html