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靜態函數輸出序列元素
    數學上的斐波那契數列是用遞歸表示的:F0=0,F1=1,Fn=Fn1+Fn2F_0=0,F_1=1,F_n=F_{n-1}+F_{n-2}
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. 我們還應瞭解到,序列可以看作是被增加限制了的列表或數組。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章