-
求函數特徵,啥是函數特徵,就是函數是什麼類型,特徵是一個專業名詞而已
namespace Library1 type Color = |Red |Green |Blue type Type0() = member type0.method1()= printfn"te" //書上代碼太多,至此爲止我們還沒學這麼多,我感覺沒有意義,所以只打了這兩個。 type Color = | Red | Green type Type0 = class new : unit -> Type0 member method1 : unit -> unit end
格式如上。
-
使用泛型判斷兩個參數是否是相同的函數
引入泛型的概念,泛型就是沒有指定變量是什麼類型,機器自己也不知道,但是有可能通過你給的式子推斷出來。 用‘T ’A ‘a 表示是泛型。let argEq x y= x = y
val argEq : x:'a -> y:'a -> bool when 'a : equality //意思是當泛型相等時具備比較功能
分析上面的結果,首先傳入的x y 是沒有指定類型的,並且最重要的是系統也推斷不出是是什麼類型,所以判斷爲泛型x:'a -> y:'a ;但是最後的結果一定是布爾類型bool when 'a : equality ,而且是在這種泛型’a與那種泛型’a一樣的時候。
此時計算機認爲的泛型與泛型一定一樣。let x1 = argEq 1 1 let x2 = argEq '1' '2'
//val x1 : bool = true //此處類型相同可比較,比較值爲true
//val x2 : bool = false//此處類型相同可比較,比較值爲falselet x3 = argEq 1 '1'//此處系統報錯,因爲類型不同,不能比較
//myfsharp1.fs(5,18): error FS0001: 此表達式應具有類型
“int”
而此處具有類型
“char” -
函數裏部分參數被用了,系統會怎麼樣表示參數的類型呢?
let add x y z = x + y + z
//val add : x:int -> y:int -> z:int -> int 三個變量的類型,表示接受3個int類型參數,返回一個Int類型結果。沒有()
此時把第一個變量給佔了:let addA = add 6
//val addA : (int -> int -> int) 表示接受兩個Int參數,返回一個int類型結果。有()表示有些參數被佔用了,也就是所謂的部分應用
注意這裏是重新定義了一個函數,這個函數是佔用了初始時函數的第一個參數形成的
再繼續進行相同的操作let addB = addA 8
//val addB : (int -> int) 表示addB函數接收一個int參數,返回一個int類型結果
再繼續let result = addB 15 printf "result = %i" result
這就是賦值語句了
//val result : int = 29
val it : unit = () -
部分應用:將不同類型的參數合在一起變成串類型
先定義函數:let add (x:int) (y:string) (z:float) = string(x)+ y + string(z) //val add : x:int -> y:string -> z:float -> string
應用第一個參數:
let addA =add 6 //val addA : (string -> float -> string)
應用第二個參數;
let addB = addA "中華" //val addB : (float -> string)
得出結果語句:
let result = addB 15. //此時一定要加.因爲是浮點數,要不然報錯。 //val result : string = "6中華15" printf "result = %s" result //result = 6中華15val it : unit = ()
-
返回類型是函數 的 函數
let myfun x = let add y = 2 * x + y add //val myfun : x:int -> (int -> int)返回類型是函數類型的,上面代碼的最後一句必須是add,這裏的函數類型是(int -> int) let x1 = myfun 100 //val x1 : (int -> int),返回的還是函數類型,這個函數正好是接受int類型,返回int類型 let x2 = x1 10 //val x2 : int = 210,返回的是int類型且這個值是210,沒有接受值,因爲已經賦值好了
-
延遲求值lazy
let x = 10 //val x : int = 10直接求出來10 let result = x + 10 //val result : int = 20。直接求出來result爲20 let result = lazy( x + 10) //val result : Lazy<int> = 未創建值。延遲求值,得不到20
那如果非要求 已經被延遲求值的值呢? 用Force。
let x1 = result.Force() //val x1 : int = 20,用來強制執行之後,以後對result的所有調用都是直接返回20,不在執行任何代碼
-
匿名函數,用(fun)實現
let x = (fun x y ->x + y) //val x : x:int -> y:int -> int,直接返回匿名函數的類型,也即是函數特徵。
匿名是什麼意思呢?就是不專門給他起個名字,不專門寫一個=,直接給fun當名字,用->表示函數實現部分。
fun也可以用function實現,唯一區別是fun不能部分應用,function可以部分應用。
我們傳入實參試試看let x = (fun x y ->x + y) 2 3 //val x : int = 5
-
自定義運算符
let ( +* ) x y = x + y //val ( +* ) : x:int -> y:int -> int。就是函數的意思
運用這個函數試試看?
let y = 6 +* 8 //val y : int = 14。得出的值符合我們自己定義的運算符的意思。
-
函數複合(<< , >>)
let (>>) f g x = g(f x) //val ( >> ) : f:('a -> 'b) -> g:('b -> 'c) -> x:'a -> 'c這裏的a是x的泛型,b是f函數的返回值泛型,c是g函數返回值的泛型,最終是由a得到c。
還記得前面學的自定義運算符嗎?可以把>>這個運算符理解爲我們自定義的,寫了三個參數,但此處有個規定只能傳入一個參數所以默認最後一個爲真正輸入的參數,前面兩個爲運算。怎麼運算呢?先算f(x)算好之後,把f(x)的值當成輸入繼續算(所以這裏要求函數g的接收值類型必須和f的輸出值一樣g(f(x))。於是>>就完成了函數複合。想要__多個參數的運算並複合__,需要用到__元組__,我們後面再說。
let f x = float (x+2) //val f : x:int -> float定義f函數 let g u = u * u + 2. * u - 6. //val g : u:float -> float定義g函數 let y = f>>g //val y : (int -> float)定義f和g複合函數y,也可以不定義直接複合使用 let x1 = y 1 //val x1 : float = 9.0使用定義好的複合函數y let x2 = (f>>g) 1 //val x2 : float = 9.0直接使用複合的語法
以上是使用>>來實現複合的
F#還有一種運算符<<,也可以用來函數複合let (<<) f g x = f(g x) //val ( << ) : f:('a -> 'b) -> g:('c -> 'a) -> x:'c -> 'b let f x = float (x+2) let g u = u * u + 2. * u - 6. let y = g<<f let x1 = y 1 let x2 = (g<<f) 1
//val f : x:int -> float
//val g : u:float -> float
//val y : (int -> float)
//val x1 : float = 9.0
//val x2 : float = 9.0
發現了什麼?一模一樣。怎麼理解?
>> 代表自左向右 f>>g 就是把f結果帶入g中;<< 代表自右向左,g<<f 就是把f結果帶入g中 -
用管道符表示函數複合( |> , <|)
let x1 = "中國" let x2 = String.replicate 3 x1 let x3 = String.length(x2)
//val x1 : string = “中國”
//val x2 : string = “中國中國中國”
//val x3 : int = 6
這是普通的兩個步驟1.複製3遍 2.求長度
以下是複合方式:let x4 = "中國"|>String.replicate 3 |>String.length //val x4 : int = 6
前面的類型必須複合後面的類型,不在贅述。
同理,<|是從右往左運算的意思let x1 = String.length <|(String.replicate 3 <|"中國") //val x1 : int = 6,爲增加可讀性,寫成分段的模式,並且**必須加()**,以保證運算正確。
-
unit類型
因爲F#是強類型語言,就是每個東西必須明確說好是什麼類型,但是有些類型不好說,比如:printfn"你好,世界!" //你好,世界!
val it : unit = ()
這個怎麼辦呢?
F#就認爲printfn函數返回值類型是unit類型的,並且這個類型只能是(),即unit = ()。 -
小功能:忽略警告信息
let x = "中華人民共和國" x
//x單獨成行會有警告信息,不是unit類型
如何忽略?
- 方法1
//val x : string = “中華人民共和國”let x = "中華人民共和國" x x|>ignore
val it : unit = () - 方法2
// val x : string = “中華人民共和國”let x = "中華人民共和國" x ignore(x)
val it : unit = () - 方法3
//val x : string = “中華人民共和國”let x = "中華人民共和國" x let _ = x
必須用_
來實現,_表示通配符。用其他字母會成爲string類型,不是unit類型。
我們來看下ignore的函數特徵
//val it : unit = ()無論接受什麼都是unit類型,所以書上寫得是T->unit。ignore()
- 用()表示unit類型,和其餘函數沒區別
//val myfun : unit -> unitlet myfun () = printfn"%s""輸出類型爲unit"