-
求函数特征,啥是函数特征,就是函数是什么类型,特征是一个专业名词而已
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"