【F#初学者】第二讲 F#中的类型简介

在第一讲中,我们介绍了F#的基本语法,包括有些特殊的分支,循环等。

在本讲中,我们将来了解F#中的类型系统,以及一些C#中没有的特殊类型系统

常见类型

类型

.NET 类型

说明

示例

bool

Boolean

可能值为 true 和 false

true,false
byte

Byte

从 0 到 255 之间的值。

1uy
sbyte

SByte

从 -128 到 127 之间的值。

1y
int16

Int16

从 -32768 到 32767 之间的值。

1s
uint16

UInt16

从 0 到 65535 之间的值。

1us
int

Int32

值的范围是 -2,147,483,648 至 2,147,483,647。

1
uint

UInt32

从 0 到 4,294,967,295 之间的值。

1u
int64

Int64

从 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 之间的值。

1L
uint64

UInt64

从 0 到 18,446,744,073,709,551,615 之间的值。

1UL
nativeint

IntPtr

作为带符号整数的原生指针。

nativeint 1
unativeint

UIntPtr

作为无符号整数的原生指针。

unativeint 1
decimal

Decimal

至少具有 28 个有效数字的浮点数据类型。

1.0m
float,double

Double

64 位浮点类型。

1.0
float32,single

Single

32 位浮点类型。

1.0f
char

Char

Unicode 字符值。

'c'
string

String

Unicode 文本。

"str"

特殊的unit类型

F#的表达式必须有一个值,unit则代表了这种情况(类似于void,但unit是一个实打实的类型),如果一个表达式返回了非unit类型,且值没有在代码中被使用,编译器便会发出警告。

集合类型

在F#中,有三种类似于数组/列表的类型,Array(数组), List(列表), Sequences(序列)

其中数组与列表的关系,与C#中的概念有一定差异,Array是固定长度,元素默认可变的数组(集合)。而List则是元素默认不可变的,当追加或删除元素时,会新生成一个list,而不会更改原有数据(但其内部元素依然是公用的),这也是F#对不可变性的推崇,不可变往往是默认行为。

let a1 = [|1; 2; 3|] 
let a2 = 
    [|1
      2
      3|]
//使用[| |]定义数组, 请注意使用;或者换行分隔数组元素
//否则会认为是一个元组的元素的数组


let l1 = [1; 2] 
// 同样的,使用[]定义列表,
let l2 = l1 @ [3; 4]
// 使用@连接两个列表
let l3 = 1 :: l2
// 使用::添加元素到列表头部
l3.[3] <- 5 // 这是不被允许的

图片

如图 array的元素默认可以改变(这其实有违不可变性,必须谨慎使用!)

图片

可以看到修改不会修改原来的列表,会生成一个新的列表,既然元素本就不可变,所以公用元素也不会造成任何的并发问题

(同时,推荐使用.[]的语法来访问数组或者列表元素,尽管最新语法中.不是必须的,但依然不推荐使用)

而Seq,则是一个惰性的序列,当我们有可能不会用到Seq的全部或是部分元素时,我们就可以采用Seq(类似于C#中的IEnumrable)

let s1 = seq { 0..10..100 }
// 使用序列表达式生成序列
let s2 = seq { for i in 0..10 -> i + 1 }
// 使用for循环->生成序列
let s3 = seq {
    for i in 0..10 do
        yield i + 1
}
// 通过yield关键字生成序列
let s4 = seq {
    for i in 0..10 do
        yield! seq {1; 2; 3; 4}
}
// 使用yield!关键字生成平铺后的序列

printfn "%A" (s1 |> Seq.toList)
printfn "%A" (s2 |> Seq.toList)
printfn "%A" (s3 |> Seq.toList)
printfn "%A" (s4 |> Seq.toList)
// 因为序列是惰性的,所以需要使用Seq.toList将序列转换为列表

惰性计算同样是在FP中非常重要的一个概念(尽管其他算法中也常会提到这个概念)

其余还有类似Map, Set的类型,同样的,默认也是不可变,即新增元素会重新生成一个新的实例 

let m1 = Map.ofList [(1, "a"); (2, "b")]
let m2 = m1 |> Map.add 3 "c"
let set1 = Set.ofList [1; 2; 3]
let set2 = Set.add 4 set1

图片

记录/匿名记录

与C#的记录类似(或是应该说C#记录与F#类似),记录是简单值的聚合。尽管在F#中,支持对记录做一定的功能扩展,但在程序中依然只是简单值的聚合,一般来说不推荐将记录复杂话,经作为数据存在在程序中(数据与逻辑分离)

type Point = { X: float; Y: float; Z: float } 
// 定义一个Point的记录类型
let mypoint3D = { X = 1.0; Y = 1.0; Z = 0.0 } 
// 通过记录表达式创建一个Point类型的值
let mypoint3D = { Point.X = 1.0; Y = 1.0; Z = 0.0 } 

let anonPoint3D = {| X1 = 1.0; Y1 = 1.0; Z1 = 0.0 |} // 匿名记录类型 

类型推断系统,会自动推断出你使用的类型(若有字段定义完全相同的类型,则以后定义的为默认情况。这种情况下可以显式定义字段)

学会使用记录来定义模型,是一件非常重要的事情,这不仅仅在F#中有用,C#的记录也意图发挥同样的作用,关于这一点,我们会专门写明一讲去介绍相关内容。在此处,我们只需记住记录是在FP中非常常用的类型,FP中通常情况不会使用 类 这个东西,他是OOP的东西

可区分联合

来到了我们重点中的重点,可区分联合。这种类型提供了强大的抽象能力,是FP中非常重要的存在。C#也正在引入这个特性,我们有可能在C#14中见到他。

简单来说,可区分联合,可以将各种类型组合在一起形成一个大类型,在类型论中这个新的类型叫做和类型,能够清晰地表达复杂的数据结构和逻辑。可以用于替代OOP中复杂的继承系统,也是monad的基础。其强大的表达能力,配合模式匹配,在FP中发挥了极其重要的作用。

type Address = 
| Email of string
| Phone of string
| QQ of int


let sendMessage addr msg = 
    match addr with
    | Email email -> printfn "Send email to %s: %s" email msg
    | Phone phone -> printfn "Send message to %s: %s" phone msg
    | QQ qq -> printfn "Send message to %d: %s" qq msg
// 必须处理所有的情况
   
type Maybe<'a> = 
| Just of 'a
| Nothing

type Option<'a> =
| Some of 'a
| None

面对对象的类型

F#作为多范式语言,自然也支持OOP那一套,但在最这里还是再次说明。一般只有与C#需要频繁交互的部分,才建议采用OOP的方式去编写代码,这些特性不是F#推荐去使用的。

简单例子

type Class(a) =
    member _.a = a
    member this.b = 1
    member val c = 1
    new() = Class(0)
    
let a = Class()

那么有关于类型系统的简介就到这里,再之后,就将真正步入函数式编程的核心领域了,敬请期待!

bilibili: @无聊的年 【F#初学者】第二讲 F#中的类型简介

csdn: @scixing

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值