-
列表
列表是多個相同類型元素使用符號對[ ]
所構成的式子,元素使用;
分隔。
與元組的區別- ()與[ ]
- , 與 ;
-
列表常量定義及列表元素訪問
//定義 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類型了,並且取值是()
-
自動產生列表
我們用..
雙點運算符來表示元素的取值範圍,其構成的式子叫範圍表達式。
語法:始值…增值…終值
始值小於終值時,則遞增產生元素;始值大於終值時,則遞減產生元素。系統不會默認我們遞增遞減,所以我們手動的把增值寫成正數和負數來區分遞增還是遞減。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] -
使用簡化雙點運算符和尾遞歸求素數
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 = () -
雙點運算符用於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 = () -
也可以指定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 = () -
指定字符內循環
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 = () -
與[ ]結合
我們知道[ ]是列表,但是列表裏面不填具體值,直接填一個表達式也是可以的,這樣的式子叫生成表達式。[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] -
使用通配符_代替不需要的元素
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才能一個個的統計。 -
使用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 = () -
使用::運算符將一個元素追加到列表表頭
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] -
使用@運算符合併兩個列表
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#中的通用的名稱。
-
使用 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是一個變量,用來存每一個值。 -
使用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)前開後閉檢索字符串。
- 使用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具體解釋見下圖:
- 使用 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: 此值不是一個函數,無法應用。
不加括號的話系統就識別不出來這是一個函數,所以必須加括號
- 使用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類型。
具體解釋看圖:
- 使用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]
這也實現了兩個列表元素依次運算的功能。
-
使用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有四個參數,第一個爲功能,剩下三個爲將要用到的列表。
-
使用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, “軟件工程”)]
-
使用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, FSharpList
1 xs1, FSharpList`1 xs2) 位置 E:\A_work\159\s\src\fsharp\FSharp.Core\local.fs:行號 337
在 <StartupCodeFSI_0007.main@() 位置 C:\VisualStudio\Projects\myfsharp_test1\myfsharp1.fs:行號 5
已因出錯而停止
由上可知,兩個列表必須有兩個相同的數量,要不然屬於語法錯誤。 -
使用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就是把這些符合條件的元素都聚集起來,變成列表輸出。它有兩個參數,第一個是功能,第二個就是列表了,具體的函數特徵看圖:
-
使用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根據元素運用於謂詞的結果來分,當元素結果爲真時放在第一個列表裏,爲假時放在第二個列表裏。就實現了分區。
上面的例子都是直接處理列表,接下來的函數會接觸到迭代,具體涉及到元素與元素之間的依賴關係
- 使用** 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.
-
使用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是“計算機應用基礎”,所以返回“計算機應用技術”加空字符串列表加“計算機應用基礎”,以此類推,得到最後結果: [“計算機應用基礎”; " "; “計算機應用技術”; " "; “數據庫應用”; " "; “計算機網絡”; " "; “軟件工程”] -
使用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]]
這一段代碼,我沒看懂::
,以後有機會回來補充。 -
使用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也可以這樣算,一個真正的列表參數,前面再加個函數參數,只不過沒有累加器罷了,所以函數就不要在把參數記錯啦! -
使用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 -
使用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行代碼。
- 使用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實現縮小功能,一個兩個參數,一個匿名函數,一個列表,匿名函數中怎麼實現縮小呢?列表前兩個值比較,取最小的那個值,在與列表的下一個值繼續比較……
-
使用** 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……後者就是從列表前兩個元素開始運算! -
使用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,匿名函數截取列表元素的第一個字母,那麼連在一起的意思是,對列表的每一個元素都查看其第一個字母然後按首字母排序。
現在再看其函數特徵就好理解了:
-
使用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就是連接兩個列表元素。 -
使用List.concat將多個列表連在一起
let x1 = ["我愛";"學習"] let x2 = ["F#"] let x3 = ["begin!"] let list = List.concat x1 x2 x3
結果爲:
myfsharp1.fs(5,5): error FS0003: 此值不是一個函數,無法應用。
竟然出錯了!來看看函數特徵:
原來需要seq類型的參數,這是後面要學的序列類型,所以這裏先不討論。 -
列表與其他類型轉換
其他類型包含兩類:
- 列表與集合類型,集合類型如:序列、數組轉換。
- 列表與元組轉換。
(1)使用 List.toSeq將列表轉換成序列,使用 List.ofSeq將序列轉換成列表。
(2)使用List.toArray將列表轉換爲數組,使用List.ofArray將數組轉換爲列表。
(3)使用List.zip將含有兩個單值的列表轉換成元組列表,使用List.unzip將元組列表轉換成兩個包含單值的列表.
(4)使用List.zip3將包含3個單元素的列表合併生成一個包含3個元素的元組列表。同理,使用List.unzip3將包含三個元素的元組列表拆解成包含3個單元素的列表.
- 列表的搜索功能
(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。
- 列表的算數運算
- 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
- 使用** 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]
-
將十進制整數轉換爲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實現列表逆序。 -
利用列表模式匹配對列表頭元素逐條處理
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……如圖所示:
-
函數的高階值
這個僅需記住一句話:在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 = ()