F#入门学习(十二)---序列

序列与列表均用来表示类型相同的元素的有序集合。
特点:

  • 序列中的元素是延迟计算,需要的时候才计算(因为序列可能是无限的);列表则一次生成所有元素。
  • 序列的延迟计算使它可以表示无限的数据结构。
  • 序列在F#库中定义为Microsoft.FSharp.Collections.Seq,是可枚举的数据结构,对应的.NET库为System.Collections.Generic.IEnumerable.即创建一个F#序列,就是创建了一个IEnumerable的实例。
  • Seq模块提供了多个序列操作静态函数。
  • 序列类型由类型seq<'T>表示。类型seq<'T>是IEnumerable<‘T>的类型缩写。
  • 数组、列表、集、映射等均与seq类型兼容。
  • 许多情况序列需要转换为列表来操作。
  • 序列没有直接的模式匹配,需将序列转换为Microsoft.FSharp.Collections.LazyLists。

1.使用常量表达式定义有限序列

let x1 = seq["计算机应用基础";"C语言";"计算机技术";"软件工程"]

结果为:
val x1 : seq = [“计算机应用基础”; “C语言”; “计算机技术”; “软件工程”]
[ ]括号为序列常量,并且必须是有限个数的。

  1. 使用双点表达式定义有限序列,并使用Seq.length求序列长度
let seq1 = seq{0..10..100}
let x1 = seq1   //默认情况,赋值语句不显示序列的值,仅给出类型(序列可能无限)
printf"%A" seq1

let x2 = Seq.length seq1

结果为:
seq [0; 10; 20; 30; …]val seq1 : seq
val x1 : seq
val x2 : int = 11
使用生成表达式需要使用括号{ }。

  1. 使用yield定义序列
let x1 = seq{ for i in 1..100 do yield i}

//输出序列的一种方法
for i in 0..Seq.length x1 - 1 do printf"%d; " (Seq.item i x1)

结果为:
1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; 100; val x1 : seq
val it : unit = ()
yield是产出的意思,每次把值提交的意思。
let x1 = seq{ for i in 1…100 do yield i}这句是定义序列的一种方式
定义好了之后我们输出这个序列,如何输出呢?
使用for循环从下标为0开始到序列的最后,do做打印操作,打印数字这里的数字不应该是i,应该是下标为i的序列值,所以现在用序列的专门使用下标取值方法Seq.item i,取下标为i的序列值并且最后返回

  1. 生成1到1000中偶数序列
let seq1 = seq{for i in 1 ..1000 do if i % 2 = 0 then yield i}

//输出序列元素的另一种方法
for x in seq1 do printf"%d; "x

结果为:
2; 4; 6; 8; 10; 12; 14; 16; 18; 20; 22; 24; 26; 28; 30; 32; 34; 36; 38; 40; 42; 44; 46; 48; 50; 52; 54; 56; 58; 60; 62; 64; 66; 68; 70; 72; 74; 76; 78; 80; 82; 84; 86; 88; 90; 92; 94; 96; 98; 100; 102; 104; 106; 108; 110; 112; 114; 116; 118; 120; 122; 124; 126; 128; 130; 132; 134; 136; 138; 140; 142; 144; 146; 148; 150; 152; 154; 156; 158; 160; 162; 164; 166; 168; 170; 172; 174; 176; 178; 180; 182; 184; 186; 188; 190; 192; 194; 196; 198; 200; 202; 204; 206; 208; 210; 212; 214; 216; 218; 220; 222; 224; 226; 228; 230; 232; 234; 236; 238; 240; 242; 244; 246; 248; 250; 252; 254; 256; 258; 260; 262; 264; 266; 268; 270; 272; 274; 276; 278; 280; 282; 284; 286; 288; 290; 292; 294; 296; 298; 300; 302; 304; 306; 308; 310; 312; 314; 316; 318; 320; 322; 324; 326; 328; 330; 332; 334; 336; 338; 340; 342; 344; 346; 348; 350; 352; 354; 356; 358; 360; 362; 364; 366; 368; 370; 372; 374; 376; 378; 380; 382; 384; 386; 388; 390; 392; 394; 396; 398; 400; 402; 404; 406; 408; 410; 412; 414; 416; 418; 420; 422; 424; 426; 428; 430; 432; 434; 436; 438; 440; 442; 444; 446; 448; 450; 452; 454; 456; 458; 460; 462; 464; 466; 468; 470; 472; 474; 476; 478; 480; 482; 484; 486; 488; 490; 492; 494; 496; 498; 500; 502; 504; 506; 508; 510; 512; 514; 516; 518; 520; 522; 524; 526; 528; 530; 532; 534; 536; 538; 540; 542; 544; 546; 548; 550; 552; 554; 556; 558; 560; 562; 564; 566; 568; 570; 572; 574; 576; 578; 580; 582; 584; 586; 588; 590; 592; 594; 596; 598; 600; 602; 604; 606; 608; 610; 612; 614; 616; 618; 620; 622; 624; 626; 628; 630; 632; 634; 636; 638; 640; 642; 644; 646; 648; 650; 652; 654; 656; 658; 660; 662; 664; 666; 668; 670; 672; 674; 676; 678; 680; 682; 684; 686; 688; 690; 692; 694; 696; 698; 700; 702; 704; 706; 708; 710; 712; 714; 716; 718; 720; 722; 724; 726; 728; 730; 732; 734; 736; 738; 740; 742; 744; 746; 748; 750; 752; 754; 756; 758; 760; 762; 764; 766; 768; 770; 772; 774; 776; 778; 780; 782; 784; 786; 788; 790; 792; 794; 796; 798; 800; 802; 804; 806; 808; 810; 812; 814; 816; 818; 820; 822; 824; 826; 828; 830; 832; 834; 836; 838; 840; 842; 844; 846; 848; 850; 852; 854; 856; 858; 860; 862; 864; 866; 868; 870; 872; 874; 876; 878; 880; 882; 884; 886; 888; 890; 892; 894; 896; 898; 900; 902; 904; 906; 908; 910; 912; 914; 916; 918; 920; 922; 924; 926; 928; 930; 932; 934; 936; 938; 940; 942; 944; 946; 948; 950; 952; 954; 956; 958; 960; 962; 964; 966; 968; 970; 972; 974; 976; 978; 980; 982; 984; 986; 988; 990; 992; 994; 996; 998; 1000; val seq1 : seq
val it : unit = ()
这种方法还是很好理解的。直接使用for语句遍历序列seq1里面的值。有限序列输出的简单方式。

  1. 使用->运算符来代替 do yield
    代码实现功能:使用->定义1到10的平方序列
let seq1 = seq{for i in 1 ..10 ->i*i}

//输出序列元素的另一种方法
for x in seq1 do printf"%d; "x

结果为:
1; 4; 9; 16; 25; 36; 49; 64; 81; 100; val seq1 : seq
val it : unit = ()
我们对比代码的第一句就可以看出区别了。

  1. 使用yield!直接生成序列
    使用yield或者->都是生成序列的元素,我们手动的元素转成序列,而yield!可以直接生成序列。
    以下代码使用yield!生成斐波那契无限序列,并使用Seq.nth静态函数输出序列元素
    数学上的斐波那契数列是用递归表示的: F 0 = 0 , F 1 = 1 , F n = F n − 1 + F n − 2 F_0=0,F_1=1,F_n=F_{n-1}+F_{n-2} F0=0,F1=1,Fn=Fn1+Fn2
let rec f a b = seq { yield a
                      yield! f b (a + b)
                     }
let seq1 = f 0 1
printfn "%A "seq1
for i in 0..10 do
    printf"%d;" (Seq.item i seq1)

结果为:
seq [0; 1; 1; 2; …]
0;1;1;2;3;5;8;13;21;34;55;val f : a:int -> b:int -> seq
val seq1 : seq
val it : unit = ()
f函数是个递归函数,这个递归没有递归出口,第一个数是a,随后一直执行f,a就变成了b,再执行fa就变成了前两个相加。
seq1的初始值是0,1…
虽然序列是无限的,但是我们打印的时候只打印10个。


序列有许多方法,我们会挑选几个来学。

  1. 使用Seq.empty和Seq.singleton创建序列
let seqEmpty = Seq.empty //创建一个空序列
let seqOne = Seq.singleton 10 //创建一个只有一个元素的序列
printf"%A " seqOne

结果为:
seq [10] val seqEmpty : seq<'a>
val seqOne : seq
val it : unit = ()

  1. 使用类型向上转换运算符:>将数组转换为序列
let x1 = [|1..10|]:> seq<int> //使用:>转换

let x2 = [|1..10|] |> Seq.ofArray //使用前管道运算符 |>和专门转换成序列的方法Seq.ofArray转化
printf"%A "x2

结果为:
seq [1; 2; 3; 4; …] val x1 : seq = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
val x2 : seq
val it : unit = ()
[|1…10|]是数组定义的方式,我们还没学到,仅作了解。
第一行的seq [1; 2; 3; 4; …]结果是x2值,它是经过printf"%A "x2打印出来的,后面val x1 : seq = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]是x1的值。

  1. 使用Seq.unfold生成有限序列
let seq1 = Seq.unfold (fun state-> if (state > 100)
                                   then None
                                   else Some(state,state+2)
                       )
                       0
for x in seq1 do printf"%d;"x
printf""

结果为:
0;2;4;6;8;10;12;14;16;18;20;22;24;26;28;30;32;34;36;38;40;42;44;46;48;50;52;54;56;58;60;62;64;66;68;70;72;74;76;78;80;82;84;86;88;90;92;94;96;98;100;val seq1 : seq
val it : unit = ()
我们看Seq.unfold的函数特征:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200506235017624.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0lfQU1fS0s=,size_16,color_FFFFFF,t_70
第一个参数是一个匿名函数,这个函数实现生成器:将初始值传到生成器,生成器按需生成元素,直到生成器返回None值,这些元素组合在一起形成我们需要的序列。
第二个参数是接受的初始值。
代码Some(state,state+2),state是输出到序列的元素,state+2是迭代部分。

  1. 使用Seq.cast将命名空间 System.Collections下的类型转换为序列
//创建一个空队列myQ
let myQ = System.Collections.Queue()
//入队
myQ.Enqueue("Hello")
myQ.Enqueue("World")
myQ.Enqueue("!")
//使用Seq.cast将队列转换为序列
let x:seq<string> = Seq.cast myQ
printfn"%A" x

结果为:
seq [“Hello”; “World”; “!”]
val myQ : System.Collections.Queue
val x : seq
val it : unit = ()
.NET框架中的System.Collections命名空间许多包含了类和接口,这些类和接口定义不同对象集合,例如,栈、队列、散列表等。序列静态函数Seq.cast可以将这些对象集合创建为序列。
我们来看Seq.cast的函数特征:
在这里插入图片描述
原本这些对象集合具有元素类型Object,但因为是有限个类型,可以枚举出来,并且是使用IEnumerable类型来枚举的,所以它的函数特征是把枚举型的转换成序列。

  1. 使用Seq.initInfinite函数生成无限序列并访问无限序列
//定义无限序列类型
let seqInfinite = Seq.initInfinite (fun index -> index * 
                                                    (if (index % 2 = 0) then 1
                                                     else -1))
//将无限序列转换为LazyList列表
let myLazyList = LazyList.ofSeq seqInfinite

let rec PrintList n 11 = 
    match n with 
        | 0 -> printf""
        | _ -> match 11 with 
                    | LazyList.Nil -> printf""
                    | LazyList.Cons(x,xs) -> 
                        printf"%d;"x
                        PrintList (n-1) xs
seqInfinite |> LazyList.ofSeq |> PrintList 100

这个程序需要加一个程序集,但是我试了下,能力有限,不会搞,等后面有机会补充
直接改写这个程序练习
使用seq.initinfinite函数来定义无限序列

let seqInfinite = Seq.initInfinite (fun index -> index * 
                                                       (if (index % 2 = 0) then 1
                                                       else -1))

printfn "%A" seqInfinite

结果为:
seq [0; -1; 2; -3; …]
val seqInfinite : seq
val it : unit = ()
我们可以定义一个无限长度的序列,并且输出它,因为他是延迟计算的。

  1. 使用Seq.exists函数判断序列是否满足某个谓词
let seq = {1..5}
let contain x = Seq.exists ( (=) x ) //使用部分应用
let x1 = contain 4 seq
let x2 = contain 99 seq 

结果为:
val seq : seq
val contain : x:'a -> (seq<'a> -> bool) when 'a : equality
val x1 : bool = true
val x2 : bool = false
线=先定义一个序列,再定义一个函数,这个函数实现判断给定值是否在已知序列里。
我们看Seq.exists的函数特征:
在这里插入图片描述
这里使用了部分应用,什么叫部分应用?第三个知识点,部分应用
我们可以把这里的部分应用理解为,(=)本来应该是取一个值进行if判断,形成一个返回值为bool类型的函数。但是我们直接省略了这个数,只使用了运算符,所以是部分应用。
**x是序列!**我们省略了数字,以至于在下面的函数调用过程中直接使用了数字和序列。

  1. 使用Seq.exists2 静态函数判断两个序列所有元素是否满足某个谓词
//判断序列x对应位置的元素是否大于序列y对应位置的元素
let seq1 = {1..2..99}
let seq2 = {2..2..10}
let contain x y = Seq.exists2 (fun u v -> if u > v then true
                                          else false)
                                x
                                y
let x1 = contain seq1 seq2
let x2 = contain seq2 seq1

结果为:
val seq1 : seq
val seq2 : seq
val contain : x:seq<'a> -> y:seq<'a> -> bool when 'a : comparison
val x1 : bool = false
val x2 : bool = true
学了前面的知识之后我们可以很轻易的理解Seq.exists2 的参数含义。

  1. 比较Seq.truncate和Seq.take输出序列前n个元素的区别
let mySeq = seq{for i in 1..10 -> i*i}//共有十个元素
let truncatSeq = Seq.truncate 5 mySeq//返回序列前5个元素
let takenSeq = Seq.take 5 mySeq//返回序列前5个元素

let truncatSeq2 = Seq.truncate 20 mySeq//返回序列前20个元素
let takenSeq2 = Seq.take 20 mySeq//返回序列前20个元素
//由于序列在用到的时候才会被计算,以上序列都还没被计算
//以下代码将被计算
let x1 = truncatSeq |> Seq.toArray//1
let x2 = takenSeq |> Seq.toArray//2
let x3 = truncatSeq2 |> Seq.toArray//3
let x4 = takenSeq2 |> Seq.toArray//4

1、2、3的结果为
val mySeq : seq
val truncatSeq : seq
val takenSeq : seq
val truncatSeq2 : seq
val takenSeq2 : seq
val x1 : int [] = [|1; 4; 9; 16; 25|]
val x2 : int [] = [|1; 4; 9; 16; 25|]
val x3 : int [] = [|1; 4; 9; 16; 25; 36; 49; 64; 81; 100|]
而第4点会报错:
System.InvalidOperationException: tried to take 输入序列具有的元素数目不足。 10 past the end of the seq
分析得出:
对于Seq.truncate求前n个元素时不会因为个数原因报错,而Seq.take如果个数超出了序列的长度是要报错的。

  1. 使用Seq.pairwise构造元组序列(数组)和使用Seq.windowed构造数组序列(数组)
let mySeq = seq{for i in 1..10 -> i*i}

let seqPairwise = Seq.pairwise mySeq
let x1 = seqPairwise |> Seq.toArray//构造元组序列

let seqWindowed = Seq.windowed 3 mySeq
let x2 = seqWindowed |> Seq.toArray//三个值一起构成数组序列

结果为:
val mySeq : seq
val seqPairwise : seq<int * int>
val x1 : (int * int) [] =
[|(1, 4); (4, 9); (9, 16); (16, 25); (25, 36); (36, 49); (49, 64); (64, 81);
(81, 100)|]
val seqWindowed : seq<int []>
val x2 : int [] [] =
[|[|1; 4; 9|]; [|4; 9; 16|]; [|9; 16; 25|]; [|16; 25; 36|]; [|25; 36; 49|];
[|36; 49; 64|]; [|49; 64; 81|]; [|64; 81; 100|]|]
我们看到Seq.pairwise是两个两个生成元组,把元组组合在一起形成了序列,Seq.windowed是自己定义个数,然后把这么多个元素组合成数组,形成序列。
他们的共同点是都是从原始序列的第一个起形成相应个数的元组或数组,再从原始序列的第二个开始,形成相应的元组或数组,例如(1, 4); (4, 9); (9, 16); (16, 25)……[|1; 4; 9|]; [|4; 9; 16|]; [|9; 16; 25|]; [|16; 25; 36|]……都是从1、4、9……开始的。

  1. 使用 Seq.compareWith比较两个序列
let x1 = Seq.compareWith compare [1;2] [1;2]
let x2 = Seq.compareWith compare [1;2;5] [1;3;2]
let x3 = Seq.compareWith compare [1] []
let x4 = Seq.compareWith compare [] [1]
let x5 = Seq.compareWith compare [1;2;5] [1;2;5;9]

结果为:
val x1 : int = 0
val x2 : int = -1
val x3 : int = 1
val x4 : int = -1
val x5 : int = -1
当两个序列完全一样是=时返回值时0,两个列表一样长,第一个不一样的地方前者小则返回-1,前者大返回1,空序列是属于小的,子序列也是小的。
注意 Seq.compareWith的函数特征:
在这里插入图片描述
第一个参数compare相当于函数。

  1. 使用Seq.countBy求给定序列前3个字符为“计算机”的课程门数和前3个字符不为“计算机”的课程门数
let mySeq = seq["计算机应用基础";"C语言";"计算机技术";"软件工程"]
let x = Seq.countBy (fun (i:string) -> 
                          match i with 
                            | i when i.Substring(0,3)="计算机" -> "计算机"
                            | _ -> "其他"
                            )
                    mySeq
printfn"%A "x

结果为:
seq [(“计算机”, 2); (“其他”, 2)]
val mySeq : seq = [“计算机应用基础”; “C语言”; “计算机技术”; “软件工程”]
val x : seq<string * int>
val it : unit = ()
Seq.countBy的函数特征是:
在这里插入图片描述
其返回值是序列,序列里面是一对值,第二个值是满足条件的个数。

  1. 使用Seq.groupBy将给定序列分组
let mySeq = seq{1..100}
let x = Seq.groupBy ( fun i ->
                             if (i % 3 = 0) then 0
                             elif ( i % 3 = 1) then 1
                             else 2)
                    mySeq
printfn"%A " x

结果为:
seq
[(1, seq [1; 4; 7; 10; …]); (2, seq [2; 5; 8; 11; …]);
(0, seq [3; 6; 9; 12; …])]
val mySeq : seq
val x : seq<int * seq>
val it : unit = ()
我们来看Seq.groupBy的函数特征:
在这里插入图片描述
它和第17个知识点很相似,最后返回一个序列,该序列内部依然是一对值(键值),键是Seq.groupBy的第一个参数决定的,值是原始序列中满足条件的元素组合(子序列)。

  1. 使用Seq.distinc创建消除重复元素的新序列
let mySeq = seq[10;2;10;2;3]
let x = Seq.distinct mySeq
printfn"%A "x

结果为:
seq [10; 2; 3]
val mySeq : seq = [10; 2; 10; 2; 3]
val x : seq
val it : unit = ()

  1. 使用Seq.distincBy消除指定重复元素
let mySeq = seq{-2..5}
printfn"%A "mySeq
let mySeqLength = Seq.length mySeq

let x = Seq.distinctBy (fun i -> abs i) mySeq
printfn"%A "x
let xLength = Seq.length x

结果为:
seq [-2; -1; 0; 1; …]
seq [-2; -1; 0; 3; …]
val mySeq : seq
val mySeqLength : int = 8
val x : seq
val xLength : int = 6
该程序消除了序列中绝对值相同的元素。我们按照其匿名函数把所有原始序列中的原始绝对值都求出来了,求的过程中,遇到1的绝对值和-1的绝对值重合所以消除,后又发现2的绝对值和-2的绝对值重合所以消除,后面就都没有重复的了,都保留。

  1. 我们还应了解到,序列可以看作是被增加限制了的列表或数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值