F#入門學習(十一)---列表

  1. 列表
    列表是多個相同類型元素使用符號對[ ] 所構成的式子,元素使用; 分隔。
    與元組的區別

    • ()與[ ]
    • , 與 ;
  2. 列表常量定義及列表元素訪問

    //定義
    let list1 = [0;2;4]
    //訪問
    let x1 = list1.[0]              //1
    let x2 = list1.[2]              //2
    let x3 = List.nth list1 1    //3
    

    val list1 : int list = [0; 2; 4]
    val x1 : int = 0
    val x2 : int = 4
    val x3 : int = 2
    可以看到,列表的下標是從0開始的,1和2使用.來訪問元素,3用列表靜態函數來訪問。List.nth有兩個參數,一個是列表名,一個是下標索引。下圖是這個函數的介紹。
    在這裏插入圖片描述
    下面是另外一種定義列表的方法:

    //定義
    let list1 = [0
                 2
                 4]
    //訪問
    let x1 = list1.[0]
    let x2 = list1.[2]
    

    結果同樣爲:
    val list1 : int list = [0; 2; 4]
    val x1 : int = 0
    val x2 : int = 4
    注意寫程序時用tab鍵把元素對齊。

    //定義空列表
    let list1 = []
    

    val list1 : 'a list
    這是空列表的定義方法,`a代表泛型。
    列表有空列表,元組有沒有呢?
    我執行了這條語句 let x1 = ()
    得到:val x1 : unit = ()
    翻了前面的書,想到我們之前學了一種類型叫:unit類型.這種類型只能取值(),就是它唯一的常量。所以不存在空元組。 由於F#是強類型語言,每個語句都有類型,但是有些語句不好安排類型,比如printf"…"這條語句,他就被安排成unit類型了,並且取值是()

  3. 自動產生列表
    我們用..雙點運算符來表示元素的取值範圍,其構成的式子叫範圍表達式。
    語法:始值…增值…終值
    始值小於終值時,則遞增產生元素;始值大於終值時,則遞減產生元素。系統不會默認我們遞增遞減,所以我們手動的把增值寫成正數和負數來區分遞增還是遞減。

    let list2 = [1..3..11]
    let list3 = [10..-4..3]
    let list4 = [4..7]  //省略增值,默認增值爲1
    

    結果爲:
    val list2 : int list = [1; 4; 7; 10]
    val list3 : int list = [10; 6]
    val list4 : int list = [4; 5; 6; 7]

  4. 使用簡化雙點運算符和尾遞歸求素數

    let prime n = 
        //定義遞歸函數ldf,求能整除n的最小數k
        let rec ldf k n = 
            if k*k > n then n
            elif n % k = 0 then k
            else ldf (k+1) n
        if (ldf 2 n) = n then printf "%d " n 
        //如果能整除n的最小數是n自己,它就是素數
    
    List.map prime [2..100] |> ignore
    

    結果爲:
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 val prime : n:int -> unit
    val it : unit = ()

  5. 雙點運算符用於for…in表達式

    let function1() = 
        for i in 1..10 do 
            printf"%d " i
        printf""
    function1()
    

    結果爲:
    1 2 3 4 5 6 7 8 9 10 val function1 : unit -> unit
    val it : unit = ()

  6. 也可以指定skip的值來循環

    let function2() = 
        for i in 1..2..10 do 
            printf"%d " i
        printf""
    function2()
    

    結果爲:
    1 3 5 7 9 val function2 : unit -> unit
    val it : unit = ()

  7. 指定字符內循環

    let function3() = 
        for i in 'a'..'z' do 
            printf"%c " i
        printf""
    function3()
    

    結果爲:
    a b c d e f g h i j k l m n o p q r s t u v w x y z val function3 : unit -> unit
    val it : unit = ()

  8. 與[ ]結合
    我們知道[ ]是列表,但是列表裏面不填具體值,直接填一個表達式也是可以的,這樣的式子叫生成表達式。

    [for i in 1..5 -> 2 * i]
    

    結果爲:
    val it : int list = [2; 4; 6; 8; 10]
    ->表示依次產生一個列表元素。
    還可以使用do yield來實現生成表達式

    [for i in 1..5 do yield 2 * i]
    

    結果爲:
    val it : int list = [2; 4; 6; 8; 10]

  9. 使用通配符_代替不需要的元素

    let list1 = [for i in 1..5 -> i * i]
    let mutable count = 0
    for _ in list1 do
       count <- count + 1
    printf"list1中元素個數爲:%d" count
    

    結果爲:
    list1中元素個數爲:5val list1 : int list = [1; 4; 9; 16; 25]
    val mutable count : int = 5
    val it : unit = ()
    _什麼也沒做。所有變量count才能一個個的統計。

  10. 使用for…in完成列表循環

    let list1 = [0..5..30]
    for i in list1 do
        printf"%d;"i
    printf"\n"
    

    結果爲:
    0;5;10;15;20;25;30;
    val list1 : int list = [0; 5; 10; 15; 20; 25; 30]
    val it : unit = ()

  11. 使用::運算符將一個元素追加到列表表頭

    let list3 = [4..11]
    let list4 = 1 :: list3
    

    結果爲:
    val list3 : int list = [4; 5; 6; 7; 8; 9; 10; 11]
    val list4 : int list = [1; 4; 5; 6; 7; 8; 9; 10; 11]

  12. 使用@運算符合併兩個列表

    let list1 = [2;4;6]
    let list2 = 0 :: list1
    let list3 = list1@list2
    

    結果爲:
    val list1 : int list = [2; 4; 6]
    val list2 : int list = [0; 2; 4; 6]
    val list3 : int list = [2; 4; 6; 0; 2; 4; 6]
    基本上常用的就是這兩個運算了。


列表靜態成員函數及其應用
前面我們學過,實例成員和靜態成員。
實例成員:實例好的名稱,加.引用方法。
靜態成員:也是加.引用方法,不過是沒有實例化的名稱,也就是一種抽象的本來就存在F#中的通用的名稱。

  1. 使用 List.init創建列表操作

    let myList = List.init 3(fun i -> string(i * 2))
    

    結果爲:
    val myList : string list = [“0”; “2”; “4”]
    在這裏插入圖片描述
    在這裏插入圖片描述
    List就是靜態函數,不是實例的。init是初始化,3是個數。
    fun i -> string(i * 2)是匿名函數,i默認從0開始,然後把數字轉成string類型,i是一個變量,用來存每一個值。

  2. 使用List.filter求滿足給定謂詞的元素(就是過濾包含關鍵詞的元素)
    求以“計算機”開頭的課程。

let names = ["計算機應用基礎";"計算機應用技術";"數據庫應用";"計算機網絡";"軟件工程"]
let jNames = 
    List.filter (fun (name:string)-> name.Substring(0,3) = "計算機") names

結果爲:
val names : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
val jNames : string list = [“計算機應用基礎”; “計算機應用技術”; “計算機網絡”]
在這裏插入圖片描述
filter函數接收兩個參數一個是bool類型的參數,檢驗前3個字是否是計算機,第二個參數是待檢測的列表名稱。總體實現的功能是:從某給定的列表裏面篩選出來符合條件的元素,組成新的列表。
在這裏插入圖片描述
Substring方法的功能是:從下標[0,3)前開後閉檢索字符串。

  1. 使用List.choose實現篩選的例子
    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";"計算機網絡";"軟件工程"]
    let delNoComputer = 
        List.choose
            (fun (x: string)->
                match x with
                | x when x.Substring(0,3)= "計算機" -> Some(x)
                | _ ->None)
          courses
    
    結果爲:
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
    val delNoComputer : string list = [“計算機應用基礎”; “計算機應用技術”; “計算機網絡”]
    看着程序有點長,其實很簡單,第一行是定義。第二行就運用了List.choose方法,該方法也是兩個參數,把第二個參數也就是指定好的列表放在第一個參數裏面選擇,如何選擇呢?就是匿名函數fun的事情了。
    fun函數把拿到的列表x進行模式匹配,和上面14點一樣選出前三個字是計算機的元素。
    Some(x)是最終符合條件的所有元素,保存之後返回。
    List.choose具體解釋見下圖:
    在這裏插入圖片描述
  2. 使用 List.iter將定義的函數應用於列表的每個元素
    遍歷列表每個元素並打印
    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";"計算機網絡";"軟件工程"]
    let printCourse c = 
        List.iter(printfn"%s") c
    printCourse courses
    
    結果爲:
    計算機應用基礎
    計算機應用技術
    數據庫應用
    計算機網絡
    軟件工程
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
    val printCourse : c:string list -> unit
    val it : unit = ()
  • 我們來看 List.iter(printfn"%s") c這一回,iter是iteration的縮寫,中文意思是迭代的意思,迭代就是一個一個應用的意思。這個方法有兩個參數,第一個參數是一個具有某功能的語句,第二個參數是列表,總體實現的功能是把功能放在每個元素上用一用。具體解釋看圖。
  • 最終返回結果是unit類型
    在這裏插入圖片描述
  • 我們發現第一個參數加了括號,那不加括號可以嗎?
    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";"計算機網絡";"軟件工程"]
    let printCourse c = 
        List.iter printfn"%s" c
    printCourse courses
    
    報錯信息爲:
    myfsharp1.fs(3,5): error FS0003: 此值不是一個函數,無法應用。
    不加括號的話系統就識別不出來這是一個函數,所以必須加括號
  1. 使用List.map對列表進行變換
    let lst1 = [6..10]
    let int2str = 
        List.map
            (fun i -> string (2*i))
            lst1
    
    結果爲:
    val lst1 : int list = [6; 7; 8; 9; 10]
    val int2str : string list = [“12”; “14”; “16”; “18”; “20”]
  • List.map該方法也是兩個參數,和 List.iter很相似,第一個參數是加括號的函數,第二個參數是列表。
  • 功能是把函數應用於每個列表元素
  • 但是List.map返回結果是列表類型,這點與List.iter不同,List.iter返回結果是unit類型。
    具體解釋看圖:
    在這裏插入圖片描述
  1. 使用List.map2對兩個列表進行變換
let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";"計算機網絡";"軟件工程"]
let scores = [4;3;5;6;5]
let x1 = List.map2
                  (fun course (score:int) -> course + "學分爲:" + string score)
                  courses
                  scores

結果爲:
val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
val scores : int list = [4; 3; 5; 6; 5]
val x1 : string list =
[“計算機應用基礎學分爲:4”; “計算機應用技術學分爲:3”; “數據庫應用學分爲:5”; “計算機網絡學分爲:6”; “軟件工程學分爲:5”]
List.map2接受三個參數,第一個是給定函數,第二個第三個是對應列表。
看了前兩個例子很好理解這個了,直接看截圖:
在這裏插入圖片描述
注意這裏截圖中泛型參數是根據我們寫好的語句判斷顯示出來的,如果直接看這個靜態函數的話如下圖:
在這裏插入圖片描述
知道了這個函數的意思之後,我們不用匿名函數,直接用運算符寫個代碼

let lst1 = [1;3;5;7;9]
let lst2 = [2;4;6;8;10]
let lst3 = List.map2 (+) lst1 lst2

結果爲:
val lst1 : int list = [1; 3; 5; 7; 9]
val lst2 : int list = [2; 4; 6; 8; 10]
val lst3 : int list = [3; 7; 11; 15; 19]
這也實現了兩個列表元素依次運算的功能。

  1. 使用List.map3對3個列表處理

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
                    "計算機網絡";"軟件工程"]
    let scores = [4;3;5;6;5]
    let semesters = ["第一學年第1學期";"第一學年第2學期";
                    "第二學年第1學期";"第二學年第2學期";"第三學年第1學期"]
    let x1 = List.map3
                    (fun course score semester ->
                    printf"課程%s的學分是%s,開課學期爲%s。"course (string score) semester)
                    courses
                    scores
                    semesters
    

    結果爲:
    課程計算機應用基礎的學分是4,開課學期爲第一學年第1學期。課程計算機應用技術的學分是3,開課學期爲第一學年第2學期。課程數據庫應用的學分是5,開課學期爲第二學年第1學期。課程計算機網絡的學分是6,開課學期爲第二學年第2學期。課程軟件工程的學分是5,開課學期爲第三學年第1學期。val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
    val scores : int list = [4; 3; 5; 6; 5]
    val semesters : string list =
    [“第一學年第1學期”; “第一學年第2學期”; “第二學年第1學期”; “第二學年第2學期”; “第三學年第1學期”]
    val x1 : unit list = [(); (); (); (); ()]
    同理,List.map3有四個參數,第一個爲功能,剩下三個爲將要用到的列表。
    在這裏插入圖片描述

  2. 使用List.mapi函數
    List.mapi函數是什麼意思?就是默認把一個列表元素索引顯示出來,處理的時候就處理一個列表,這個索引標號是默認輸出的。也就是說,只有一個參數。具體理解可以看下面的例子。

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
              			   "計算機網絡";"軟件工程"]
    let x1 = List.mapi (fun i course -> (i , course)) courses
    

    結果爲:
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
    val x1 : (int * string) list =
    [(0, “計算機應用基礎”); (1, “計算機應用技術”); (2, “數據庫應用”); (3, “計算機網絡”); (4, “軟件工程”)]
    在這裏插入圖片描述

  3. 使用List.mapi2函數
    先看代碼

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
                    "計算機網絡";"軟件工程"]
    let semesters = ["第一學年第1學期";"第一學年第2學期";
                 "第二學年第1學期";"第二學年第2學期";"第三學年第1學期"]
    let x1 = List.mapi2
                   (fun i fist last -> (i,sprintf"%s %s"fist last))
                   courses semesters
    

    結果爲:
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
    val semesters : string list =
    [“第一學年第1學期”; “第一學年第2學期”; “第二學年第1學期”; “第二學年第2學期”; “第三學年第1學期”]
    val x1 : (int * string) list =
    [(0, “計算機應用基礎 第一學年第1學期”); (1, “計算機應用技術 第一學年第2學期”); (2, “數據庫應用 第二學年第1學期”);
    (3, “計算機網絡 第二學年第2學期”); (4, “軟件工程 第三學年第1學期”)]
    在這裏插入圖片描述
    事實上,和第19點類似,List.mapi2接收兩個列表參數,默認好了一個參數,功能是把函數應用於兩個列表元素。
    問題就出現了,萬一這兩個列表給元素個數不一樣,下邊該會出現什麼情況呢?
    我們刪掉學期中的一個,運行一下

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
                   "計算機網絡";"軟件工程"]
    let semesters = ["第一學年第1學期";"第一學年第2學期";
                "第二學年第1學期";"第二學年第2學期"]
    let x1 = List.mapi2
                  (fun i fist last -> (i,sprintf"%s %s"fist last))
                  courses semesters
    

    報錯信息爲:
    System.ArgumentException: 各個列表具有不同的長度。
    list2 is 1 element shorter than list1
    參數名: list2
    在 Microsoft.FSharp.Core.DetailedExceptions.invalidArgDifferentListLength[?](String arg1, String arg2, Int32 diff) 位置 E:\A_work\159\s\src\fsharp\FSharp.Core\local.fs:行號 24
    在 Microsoft.FSharp.Primitives.Basics.List.mapi2[T1,T2,TResult](FSharpFunc2 f, FSharpList1 xs1, FSharpList`1 xs2) 位置 E:\A_work\159\s\src\fsharp\FSharp.Core\local.fs:行號 337
    在 <StartupCodeFSI0007>.FSI_0007>.FSI_0007.main@() 位置 C:\VisualStudio\Projects\myfsharp_test1\myfsharp1.fs:行號 5
    已因出錯而停止
    由上可知,兩個列表必須有兩個相同的數量,要不然屬於語法錯誤。

  4. 使用List.collect完成選擇功能

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
                    "計算機網絡";"軟件工程"]
    let chooseComputerCourses = 
        List.collect 
            (fun (x:string) -> 
                match x with
                | x when x.Substring(0,3) <> "計算機" -> [] //<>就是不等於的意思
                | x -> [ x ])
            courses
    

    結果爲:
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
    val chooseComputerCourses : (string list -> string list)
    我們直接看模式匹配中的代碼,第一個匹配中,如果這個元素中前三個字是計算機這三個字,就把這個列表元素置空,第二個匹配中,(餘下的情況,就是前三個字是計算機)保存下來。
    List.collect就是把這些符合條件的元素都聚集起來,變成列表輸出。它有兩個參數,第一個是功能,第二個就是列表了,具體的函數特徵看圖:
    在這裏插入圖片描述

  5. 使用List.partition對列表進行分區

let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
                "計算機網絡";"軟件工程"]
let (computer,nocomputer) = 
    List.partition
        (fun (name:string)-> name.Substring(0,3) = "計算機")
        courses

結果爲:
val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
val nocomputer : string list = [“數據庫應用”; “軟件工程”]
val computer : string list = [“計算機應用基礎”; “計算機應用技術”; “計算機網絡”]

  • 我們看到程序的返回結果是一個二元元組,再看下圖,發現該函數返回結果的函數特徵是兩個列表,很顯然,最終的結果是把兩個列表分別當成元組的元素輸出的。
  • 下面我們來看List.partition函數,它有兩個參數,第一個參數是謂詞,第二個參數是列表,爲什麼返回結果就實現了分區?是根據什麼準則分區的?答案是:List.partition根據元素運用於謂詞的結果來分,當元素結果爲真時放在第一個列表裏,爲假時放在第二個列表裏。就實現了分區。
    在這裏插入圖片描述

上面的例子都是直接處理列表,接下來的函數會接觸到迭代,具體涉及到元素與元素之間的依賴關係

  1. 使用** List.fold**求串類型列表中所有元素長度
    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
                    "計算機網絡";"軟件工程"]
    let totalLength = 
        List.fold
            (fun acc (str:string) -> acc + str.Length)
            0
            courses
            ```
            
    

運行以上代碼,得到運行結果:
val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
val totalLength : int = 28
我們先來分析一下List.fold的函數特徵:
在這裏插入圖片描述
它一共有三個參數,第一個和前面學的一樣是個函數,表明要對列表各元素實施的操作,本代碼中acc和str是匿名函數的兩個參數,該匿名函數返回結果是把acc和str的長度加起來並返回。第二個是int類型參數,第三個是string類型參數。
按道理來說,我們第一次運用fun函數的時候是0+一個字符串的長度,然後結束了。但是現在這個string類型是個列表,列表有好幾個字符串,這時候功能會有怎麼的變化呢?
fold做的操作就是0+“計算機應用基礎”的個數=7,再接着執行fold:7+“計算機應用技術”的個數=14,再接着執行14+“數據庫應用”=19,再接着執行19+“計算機網絡”=24,再接着執行24+“軟件工程”=28,結束,返回28。值得注意的是每次運算得到的數字都存在acc裏面,最後fold的返回值是acc。
我們單獨來看fold這個函數的特徵
在這裏插入圖片描述
State->T->State(1)對應着函數的特徵。後面還有個State,還有個T,還有個返回值State,其實和(1)是一一對應的,一直對列表的元素迭代這個運算,並且按順序來迭代的,只有第一個元素執行完才輪到第二個元素,就是開頭說的元素之間的依賴關係了。

我們明白了原理之後,用它試個小功能加深理解。

let x1 = List.fold (+) 0 [1..10]

結果爲:
val x1 : int = 55
+的函數特徵就是a->b->c符合fold的要求,所以得到了運算結果55.

  1. 使用List.foldBack在列表元素中插入空字符串

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
    "計算機網絡";"軟件工程"]
    let spacedStrings = 
        List.foldBack
            (fun str acc -> 
                if List.isEmpty acc then [str]@acc
                else [str] @ [" "]@acc )
            courses
            []
    

    結果爲:
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
    val spacedStrings : string list =
    [“計算機應用基礎”; " "; “計算機應用技術”; " "; “數據庫應用”; " "; “計算機網絡”; " "; “軟件工程”]
    我們直接看函數特徵在這裏插入圖片描述
    我們發現,foldBack和fold的區別在於,前者每次運算都存在最後那個參數中,返回值也是返回最後一個參數,後者每次運算都是存在第二個參數裏面,最後返回值也是返回第二個參數。第二個參數是字符串,第三個參數是列表,和fold的參數相反,其餘操作大致相同。
    接下來我們來看看本代碼中匿名函數的功能:其中有個@運算符在這裏插入圖片描述
    它的函數特徵是接受兩個列表元素,返回一個列表元素,我忘記以前是不是學過這個運算符了,就猜測是把兩個列表合併,運行了這句代碼let x = [1;3;5] @ [2;4],返回結果爲:val x : int list = [1; 3; 5; 2; 4],果然是合併的意思。
    於是匿名函數判斷當累加器acc列表是空時返回列表元素加空acc列表(意思是還是原來的元素),當累加器acc不爲空時,返回列表元素加空字符串列表加非空累加器acc列表,coursers第一個元素是“計算機應用基礎” acc是空列表,所以返回acc“計算機應用基礎”,這時,coursers第二個元素是“計算機應用技術” acc是“計算機應用基礎”,所以返回“計算機應用技術”加空字符串列表加“計算機應用基礎”,以此類推,得到最後結果: [“計算機應用基礎”; " "; “計算機應用技術”; " "; “數據庫應用”; " "; “計算機網絡”; " "; “軟件工程”]

  2. 使用List.foldBack求列表的冪集
    冪集:一個集合其所有子集構成的集合,包含空集。假設集合個數爲k,則該集合的冪集個數爲2^k。

    let subsets xs = List.foldBack
                            (fun x rest ->rest @ List.map (fun ys -> x::ys) rest)
                            xs
                            [[]]
    let xx = subsets [1;2;3;4]
    

    結果爲:
    val subsets : xs:'a list -> 'a list list
    val xx : int list list =
    [[]; [4]; [3]; [3; 4]; [2]; [2; 4]; [2; 3]; [2; 3; 4]; [1]; [1; 4]; [1; 3];
    [1; 3; 4]; [1; 2]; [1; 2; 4]; [1; 2; 3]; [1; 2; 3; 4]]
    這一段代碼,我沒看懂::,以後有機會回來補充。

  3. 使用List.fold2求兩個列表元素字符串長度

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用"]
    let courses2 = ["c語言";"計算機網絡";"軟件工程"]
    let totalLength = 
        List.fold2
            (fun acc (str:string) (punc:string) -> acc + str.Length + punc.Length)
            0
            courses
            courses2
    

    結果爲:
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”]
    val courses2 : string list = [“c語言”; “計算機網絡”; “軟件工程”]
    val totalLength : int = 31
    這回我們不按照上面的方式理解了,因爲認真描述函數特徵很繁瑣,有了之前的基礎,也很容易看出來。
    List.fold2真正用到的參數就是兩個列表,所以是fold2,第一個參數是函數,第二個參數是累加器,第三第四就是列表了。用這樣的理解方式我們看到,之前學的map也可以這樣算,一個真正的列表參數,前面再加個函數參數,只不過沒有累加器罷了,所以函數就不要在把參數記錯啦!

  4. 使用List.scan求串列表元素長度累加構成的列表

    let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用"]
    let wordoffset = 
        List.scan
            (fun acc (str:string) -> acc + str.Length)
            0
            courses
    

    結果爲:
    val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”]
    val wordoffset : int list = [0; 7; 14; 19]
    這個方法很好理解,scan掃描列表的意思,每掃描完一次列表值就存一次結果。
    函數第一個參數依舊是功能,這個功能是用累加器acc存放列表str的長度
    累加器初始值是0

  5. 使用List.scanBack求串列表元素長度累加構成的列表。

let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用"]
let backwordoffset = 
    List.scanBack
        (fun(str:string) acc  -> acc + str.Length)
        courses
        0

結果爲:
val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”]
val backwordoffset : int list = [19; 12; 5; 0]
這個函數很有意思,就是反向掃描,掃描的結果也是反向存儲。比如:初始值爲[0],從數據庫應用開始統計長度,結果就成了[5;0]……直到得出結果list = [19; 12; 5; 0]。
需要注意,List.scanBack和List.scan中匿名函數的參數相反,本身後兩個參數也是相反的。具體對比第4、5、6行代碼。

  1. 使用List.reduce
  • 使用List.reduce求整型列表累加和。
let lst1 = [1..10]
let lst1Sum = 
    List.reduce
        (fun i0 i1 -> i0 + i1)
        lst1

結果爲:
val lst1 : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
val lst1Sum : int = 55
在這裏插入圖片描述
reduce翻譯爲減少不好,要翻譯成縮小,把列表裏的數都縮在一起,怎麼縮呢?以list爲例,1 2經過匿名函數加在一起爲3,3 3經過匿名函數加在一起爲6,6 4經過匿名函數加在一起爲10……最後得到最終值爲55.
List.reduce有兩個參數,一個匿名函數實現某某功能,還有一個就是準備被功能的列表了。

  • 使用List.reduce求串類型列表最短元素名稱
let courses = ["計算機應用基礎";"計算機應用技術";"數據庫應用";
    "計算機網絡";"軟件工程"]
let shortesName = 
    List.reduce
        (fun (shortest:string)(this:string)->
            if shortest.Length <= this.Length then shortest
            else this)
        courses

結果爲:
val courses : string list = [“計算機應用基礎”; “計算機應用技術”; “數據庫應用”; “計算機網絡”; “軟件工程”]
val shortesName : string = “軟件工程”
List.reduce實現縮小功能,一個兩個參數,一個匿名函數,一個列表,匿名函數中怎麼實現縮小呢?列表前兩個值比較,取最小的那個值,在與列表的下一個值繼續比較……

  1. 使用** List.reduceBack**求整型列表元素和

    let list = [1..10]
    let sumlist = 
        List.reduceBack
            (fun i0 i1 -> i0 + i1)
            list
    

    結果爲:
    val list : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
    val sumlist : int = 55
    List.reduceBack和 List.reduce類似,區別在於前者從列表後兩個元素開始運算,每次縮小爲一個,再和倒數第三個元素運算,9+10=19,8+19=27,7+27=34……後者就是從列表前兩個元素開始運算!

  2. 使用List.sortBy對列表元素指定特性排序

    open System
    let courses = ["計算機應用技術";"計算機導論";"軟件工程";"C語言"]
    let x1 = List.sortBy (fun elem ->String.length(elem)) courses
    let x2 = List.sortBy (fun (elem:string)->elem.Substring(0,1)) courses
    

    結果爲:
    val courses : string list = [“計算機應用技術”; “計算機導論”; “軟件工程”; “C語言”]
    val x1 : string list = [“C語言”; “軟件工程”; “計算機導論”; “計算機應用技術”]
    val x2 : string list = [“C語言”; “計算機應用技術”; “計算機導論”; “軟件工程”]
    List.sortBy是按照某種方式排序的意思,它接收兩個參數,一個參數是匿名函數實現排序的要求,另一個參數是列表。
    我們來看x1,匿名函數返回值就是長度,那麼連在一起的意思就是,對列表的每一個元素都求其長度然後按長度排序。
    我們再看x2,匿名函數截取列表元素的第一個字母,那麼連在一起的意思是,對列表的每一個元素都查看其第一個字母然後按首字母排序。
    現在再看其函數特徵就好理解了:
    在這裏插入圖片描述

  3. 使用List.append將兩個列表連在一起

    let x1 = ["我愛";"學習"]
    let x2 = ["F#"]
    let list = 
        List.append x1 x2
    

    結果爲:
    val x1 : string list = [“我愛”; “學習”]
    val x2 : string list = [“F#”]
    val list : string list = [“我愛”; “學習”; “F#”]
    在這裏插入圖片描述
    List.append就是連接兩個列表元素。

  4. 使用List.concat將多個列表連在一起

    let x1 = ["我愛";"學習"]
    let x2 = ["F#"]
    let x3 = ["begin!"]
    let list = 
        List.concat x1 x2 x3
    

    結果爲:
    myfsharp1.fs(5,5): error FS0003: 此值不是一個函數,無法應用。
    竟然出錯了!來看看函數特徵:
    在這裏插入圖片描述
    原來需要seq類型的參數,這是後面要學的序列類型,所以這裏先不討論。

  5. 列表與其他類型轉換
    其他類型包含兩類:

  • 列表與集合類型,集合類型如:序列、數組轉換。
  • 列表與元組轉換。
    (1)使用 List.toSeq將列表轉換成序列,使用 List.ofSeq將序列轉換成列表。
    在這裏插入圖片描述
    在這裏插入圖片描述
    (2)使用List.toArray將列表轉換爲數組,使用List.ofArray將數組轉換爲列表。
    在這裏插入圖片描述
    在這裏插入圖片描述
    (3)使用List.zip將含有兩個單值的列表轉換成元組列表,使用List.unzip將元組列表轉換成兩個包含單值的列表.
    在這裏插入圖片描述
    在這裏插入圖片描述
    (4)使用List.zip3將包含3個單元素的列表合併生成一個包含3個元素的元組列表。同理,使用List.unzip3將包含三個元素的元組列表拆解成包含3個單元素的列表.
    在這裏插入圖片描述
    在這裏插入圖片描述
  1. 列表的搜索功能
    (1)使用List.find函數查找符合給定條件的第一個元素,使用List.findIndex函數返回符合給定條件的第一個元素索引。
    在這裏插入圖片描述
    在這裏插入圖片描述
    (2)使用List.tryFind找到符合給定條件的第一個元素(此時可以把該元素當成存在的意思),若該元素不存在,則返回選項中None.使用List.tryFindIndex返回找到的元素索引,其餘與List.tryFind相同。
    在這裏插入圖片描述
    在這裏插入圖片描述
    (3)List.pick與List.find功能類似,但是前者必須先轉換元素,而不是直接返回元素。(啊!啊!啊!我看不懂這句話,難道是指必須處理得到的元素?)
    在這裏插入圖片描述
    (4)List.tryPick表明如果存在元素x滿足函數,則返回第一個結果Some(x),如果不存在此類元素,則返回None。
    在這裏插入圖片描述
  2. 列表的算數運算
  • List.sum求和(列表元素必須可以運算)
let x = [1..10]
let y = 
    List.sum x

結果爲:
val x : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
val y : int = 55

  • List.average求平均(列表元素不能是整型)
let x = [1.0 .. 10.0]
let y = 
    List.average x

結果爲:
val x : float list = [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 9.0; 10.0]
val y : float = 5.5
列表x是浮點型元素,如果是整型呢?

let x = [1 .. 10]
let y = 
   List.average x

結果爲:
myfsharp1.fs(3,18): error FS0001: 類型“int”不支持運算符“DivideByInt”
整型會出錯!

  • List.sumBy採用一個函數作爲參數,並將此函數的結果用於計算求和
let x = [1 .. 10]
let y = 
    List.sumBy
        (fun (i:int) -> i+1)
        x

結果爲:
val x : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
val y : int = 65

  • List.sumBy採用一個函數作爲參數,並將此函數的結果用於計算求平均
let x = [1.0 .. 10.0]
let y = 
    List.sumBy
        (fun (i:float) -> i*i)
        x

結果爲:
val x : float list = [1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 9.0; 10.0]
val y : float = 385.0

  1. 使用** List.rev**將列表逆序
let x = [1..10]
let y = List.rev x

結果爲:
val x : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
val y : int list = [10; 9; 8; 7; 6; 5; 4; 3; 2; 1]

  1. 將十進制整數轉換爲0、1構成的二進制列表
    首先要明白把十進制轉化爲二進制的方法,將十進制整數除以2,求其對應的餘數,將這些餘數逆序即爲其對應的二進制:
    該數除以2;
    1.若餘數爲0,把0保存進列表,把除完的整數留下當成該數
    2.若餘數爲1,把1保存進列表,把除完的整數留下當成該數
    3.重複1,2直到該數爲0或1,把得到的列表逆序。

    let Dec2Bin n =  //接收一個數
        let rec generateBinary n =  //定義遞歸函數
            if (n/2 = 0)then [n] //遞歸出口,若該數爲0或1,則返回原值結束遞歸
            else (n%2) :: generateBinary (n/2) //遞歸將餘數添加到列表中
        generateBinary n |> List.rev //將列表元素逆序
    
    let x1 = Dec2Bin 14
    let x2 = Dec2Bin 32
    let x3 = Dec2Bin 31
    

    結果爲:
    val Dec2Bin : n:int -> int list
    val x1 : int list = [1; 1; 1; 0]
    val x2 : int list = [1; 0; 0; 0; 0; 0]
    val x3 : int list = [1; 1; 1; 1; 1]
    這段代碼的::似乎更清楚了些,代表把每次的值都存進列表裏。
    |>管道運算符,把最後結果拿出來運算。
    List.rev實現列表逆序。

  2. 利用列表模式匹配對列表頭元素逐條處理

    let mylst1 = [for i in 1..3 ->i]
    let rec processLst lst1 = 
        match lst1 with
            |h :: t ->
                processLst t                //先遞歸處理列表的尾
                printfn "獲得的頭元素爲%d" h //因爲上一句是遞歸,這句只有遞歸結束才被執行
            |[]->printfn "列表爲空!"  //遞歸棧底
    
    let x1 = processLst mylst1
    

    結果爲:
    列表爲空!
    獲得的頭元素爲3
    獲得的頭元素爲2
    獲得的頭元素爲1
    val mylst1 : int list = [1; 2; 3]
    val processLst : lst1:int list -> unit
    val x1 : unit = ()
    學過數據結構的都知道,遞歸要有遞歸出口和遞歸體,遞歸體是調用自己的那些代碼,遞歸出口是最後結束的條件。
    列表模式匹配中,head::tail模式稱爲Cons模式,用於將列表分解爲第一個元素(即頭或者head)和一個包含其餘元素的列表(即尾或tail),這裏強烈推薦看數據結構的廣義表,他們是一個東西。
    模式匹配中:
    h :: t總是把列表分爲表頭和表尾, (processLst t總是遞歸的取表尾,打印該數字)放在棧裏面實現,並不真正的實現,最後出棧才行,直到取到最後一個也就是[]空列表,執行到第二個匹配式子,打印"列表爲空!"遞歸結束,開始出棧那樣的打印獲得的頭元素爲3……如圖所示:
    在這裏插入圖片描述

  3. 函數的高階值
    這個僅需記住一句話:在F#中,常量值和函數值的地位等同,常數值怎麼用函數值也可以一樣用!

    open System
    
    //將函數綁定到標識符上
    let cube x = x ** 3.0  //**是求立方
    let croot x = x ** (1.0 / 3.0)  //求立方根
    
    //將函數存儲在複合類型的數據結構中
    let funclist = [Math.Sin;Math.Cos;cube] //列表要求具有相同的函數特徵
    let funclisti = [Math.Asin;Math.Acos;croot] //反正弦,反餘弦
    
    //將函數作爲一個函數的參數,傳遞到該函數中
    let composed = List.map2 (<<) funclist funclisti //<<當成一個匿名函數的功能(後向運算),爲第一個參數。參考第18個知識點
    
    //將函數作爲一個函數調用的結果返回
    let main() = for f in composed do printfn "%f" (f 0.5)
    main()  //調用查看結果
    

    結果爲:
    0.500000
    0.500000
    0.500000
    val cube : x:float -> float
    val croot : x:float -> float
    val funclist : (float -> float) list =
    [fun:funclist@8; fun:funclist@8-1; fun:funclist@8-2]
    val funclisti : (float -> float) list =
    [fun:funclisti@9; fun:funclisti@9-1; fun:funclisti@9-2]
    val composed : (float -> float) list =
    [fun:Invoke@2686; fun:Invoke@2686; fun:Invoke@2686]
    val main : unit -> unit
    val it : unit = ()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章