函數(shù)式編程是一種編程模型,將計算機(jī)運算看作是數(shù)學(xué)中函數(shù)的計算,并且避免了狀態(tài)以及變量的概念。
在面向?qū)ο笏枷氘a(chǎn)生前,函數(shù)式編程已經(jīng)有數(shù)十年的歷史。隨著硬件性能的提升以及編譯技術(shù)和虛擬機(jī)技術(shù)的改進(jìn),一些曾被性能問題所限制的動態(tài)語言開始受到關(guān)注,Python、Ruby和Lua等語言都開始在應(yīng)用中嶄露頭角。動態(tài)語言因其方便快捷的開發(fā)方式成為很多人喜愛的編程語言,伴隨動態(tài)語言的流行,函數(shù)式編程也開始流行。
薌城網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),薌城網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為薌城1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請找那個售后服務(wù)好的薌城做網(wǎng)站的公司定做!
函數(shù)式編程的主要特點如下:
A、變量的不可變性: 變量一經(jīng)賦值不可改變。如果需要改變,則必須復(fù)制出去,然后修改。
B、函數(shù)是一等公民: 函數(shù)也是變量,可以作為參數(shù)、返回值等在程序中進(jìn)行傳遞。
C、尾遞歸:如果遞歸很深的話,堆棧可能會爆掉,并導(dǎo)致性能大幅度下降。而尾遞歸優(yōu)化技術(shù)(需要編譯器支持)可以在每次遞歸時重用stack。
在函數(shù)式編程中,函數(shù)需要作為參數(shù)傳遞,即高階函數(shù)。在數(shù)學(xué)和計算機(jī)科學(xué)中,高階函數(shù)是至少滿足下列一個條件的函數(shù):
A、函數(shù)可以作為參數(shù)被傳遞
B、函數(shù)可以作為返回值輸出
匿名函數(shù)是指不需要定義函數(shù)名的一種函數(shù)實現(xiàn)方式,匿名函數(shù)由一個不帶函數(shù)名的函數(shù)聲明和函數(shù)體組成。C和C++不支持匿名函數(shù)。
func(x,y int) int {
return x + y
}
在Go語言中,所有的函數(shù)是值類型,即可以作為參數(shù)傳遞,又可以作為返回值傳遞。
匿名函數(shù)可以賦值給一個變量:
f := func() int {
...
}
定義一種函數(shù)類型:type CalcFunc func(x, y int) int
函數(shù)可以作為值傳遞:
func AddFunc(x, y int) int {
return x + y
}
func SubFunc(x, y int) int {
return x - y
}
...
func OperationFunc(x, y int, calcFunc CalcFunc) int {
return calcFunc(x, y)
}
func main() {
sum := OperationFunc(1, 2, AddFunc)
difference := OperationFunc(1, 2, SubFunc)
...
}
函數(shù)可以作為返回值:
// 第一種寫法
func add(x, y int) func() int {
f := func() int {
return x + y
}
return f
}
// 第二種寫法
func add(x, y int) func() int {
return func() int {
return x + y
}
}
當(dāng)函數(shù)返回多個匿名函數(shù)時建議采用第一種寫法:
func calc(x, y int) (func(int), func()) {
f1 := func(z int) int {
return (x + y) * z / 2
}
f2 := func() int {
return 2 * (x + y)
}
return f1, f2
}
匿名函數(shù)的調(diào)用有兩種方法:
// 通過返回值調(diào)用
func main() {
f1, f2 := calc(2, 3)
n1 := f1(10)
n2 := f1(20)
n3 := f2()
fmt.Println("n1, n2, n3:", n1, n2, n3)
}
// 在匿名函數(shù)定義的同時進(jìn)行調(diào)用:花括號后跟參數(shù)列表表示函數(shù)調(diào)用
func safeHandler() {
defer func() {
err := recover()
if err != nil {
fmt.Println("some exception has happend:", err)
}
}()
...
}
函數(shù)可以嵌套定義(嵌套的函數(shù)一般為匿名函數(shù)),即在一個函數(shù)內(nèi)部可以定義另一個函數(shù)。Go語言通過匿名函數(shù)支持閉包,C++不支持匿名函數(shù),在C++11中通過Lambda表達(dá)式支持閉包。
閉包是由函數(shù)及其相關(guān)引用環(huán)境組合而成的實體(即:閉包=函數(shù)+引用環(huán)境)。
閉包只是在形式和表現(xiàn)上像函數(shù),但實際上不是函數(shù)。函數(shù)是一些可執(zhí)行的代碼,函數(shù)代碼在函數(shù)被定義后就確定,不會在執(zhí)行時發(fā)生變化,所以一個函數(shù)只有一個實例。閉包在運行時可以有多個實例,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實例。
所謂引用環(huán)境是指在程序執(zhí)行中的某個點所有處于活躍狀態(tài)的約束所組成的集合。約束是指一個變量的名字和其所代表的對象之間的聯(lián)系。由于在支持嵌套作用域的語言中,有時不能簡單直接地確定函數(shù)的引用環(huán)境,因此需要將引用環(huán)境與函數(shù)組合起來。
閉包是包含自由變量的代碼塊,變量不在代碼塊內(nèi)或者任何全局上下文中定義,而是在定義代碼塊的環(huán)境中定義。由于自由變量包含在代碼塊中,所以只要閉包還被使用,那么自由變量以及引用的對象就不會被釋放,要執(zhí)行的代碼為自由變量提供綁定的計算環(huán)境。
閉包可以作為函數(shù)對象或者匿名函數(shù)。支持閉包的多數(shù)語言都將函數(shù)作為第一級對象,即函數(shù)可以存儲到變量中作為參數(shù)傳遞給其它函數(shù),能夠被函數(shù)動態(tài)創(chuàng)建和返回。
func add(n int) func(int) int {
sum := n
f := func(x int) int {
var i int = 2
sum += i * x
return sum
}
return f
}
add函數(shù)中函數(shù)變量為f,自由變量為sum,同時f為sum提供綁定的計算環(huán)境,sum和f組成的代碼塊就是閉包。add函數(shù)的返回值是一個閉包,而不僅僅是f函數(shù)的地址。在add閉包函數(shù)中,只有內(nèi)部的匿名函數(shù)f才能訪問局部變量i,而無法通過其它途徑訪問,因此閉包保證了i的安全性。
當(dāng)分別用不同的參數(shù)(10, 20)注入add函數(shù)而得到不同的閉包函數(shù)變量時,得到的結(jié)果是隔離的,即每次調(diào)用add函數(shù)后都將生成并保存一個新的局部變量sum。
在函數(shù)式語言中,當(dāng)內(nèi)嵌函數(shù)體內(nèi)引用到體外的變量時,將會把定義時涉及到的引用環(huán)境和函數(shù)體打包成一個整體(閉包)返回。
當(dāng)每次調(diào)用add函數(shù)時都將返回一個新的閉包實例,不同實例之間是隔離的,分別包含調(diào)用時不同的引用環(huán)境現(xiàn)場。不同于函數(shù),閉包在運行時可以有多個實例,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實例。
從形式上看,匿名函數(shù)都是閉包。
函數(shù)只是一段可執(zhí)行代碼,編譯后就固定,每個函數(shù)在內(nèi)存中只有一份實例,得到函數(shù)的入口點便可以執(zhí)行函數(shù)。
對象是附有行為的數(shù)據(jù),而閉包是附有數(shù)據(jù)的行為。
閉包經(jīng)常用于回調(diào)函數(shù),當(dāng)IO操作(例如從網(wǎng)絡(luò)獲取數(shù)據(jù)、文件讀寫)完成的時候,會對獲取的數(shù)據(jù)進(jìn)行某些操作,操作可以交給函數(shù)對象處理。
除此之外,在一些公共的操作中經(jīng)常會包含一些差異性的特殊操作,而差異性的操作可以用函數(shù)來進(jìn)行封裝。
package main
import "fmt"
func adder() func(int) int {
sum := 0
f := func(x int) int {
sum += x
return sum
}
return f
}
func main() {
sum := adder()
for i := 0; i < 10; i++ {
fmt.Println(sum(i))
}
}
package main
import "fmt"
//普通閉包
func adder() func(int) int {
sum := 0
return func(v int) int {
sum += v
return sum
}
}
//無狀態(tài)、無變量的閉包
type iAdder func(int) (int, iAdder)
func adder2(base int) iAdder {
return func(v int) (int, iAdder) {
return base + v, adder2(base + v)
}
}
//使用閉包實現(xiàn)斐波那契數(shù)列
func Fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
func main() {
//普通閉包調(diào)用
a := adder()
for i := 0; i < 10; i++ {
var s int =a(i)
fmt.Printf("0 +...+ %d = %d\n",i, s)
}
//狀態(tài) 無變量的閉包 調(diào)用
b := adder2(0)
for i := 0; i < 10; i++ {
var s int
s, b = b(i)
fmt.Printf("0 +...+ %d = %d\n",i, s)
}
//調(diào)用斐波那契數(shù)列生成
fib:=Fibonacci()
fmt.Println(fib(),fib(),fib(),fib(),fib(),fib(),fib(),fib())
}