標籤:style java color 使用 strong os
函數和閉包1 成員方法
java中函數存在的方式,我們給一個類追加一個功能的途徑就是給他追加一個方法。
2 本地方法
所謂的本地方法就是存在於一個方法內部的方法。
如果一個類中有一個方法是private的,且只有一個方法使用到他,那麼這個方法完全可以被定義為一個本地方法
3 函數式一等公民
方法分為:
1 方法名稱 def add
2 方法參數 (a:Int, b:Int)
3 賦值符號 =
4 方法體 a + b
其實,(a:Int, b:Int) => a + b,就構成了一個基本的函數了,只不過它的名字叫做add,所以說我們可以這麼調用一個函數:
add(1, 2),傳回值為3
也可以這麼調用這個函數:
((a:Int, b:Int) => a + b)(1, 2),傳回值也是3
現在就應該相對明朗了,我們可以把一個函數賦給一個val變數,這樣這個變數就是一個函數了,如:
val addition = (a:Int, b:Int) => a + b
那麼addtion(11, 12)就等於23。
另外方法可以被當成函數的參數來使用,如List的foreach方法,它需要一個方法作為參數
val list = List(1, 2, 3, 4, 5)
list.foreach(element => println(element + 1))
另外一個filter方法,
val evenList = list.filter(element => element % 2 == 0)
4 更加簡潔
像上面的紅字部分提供了簡寫方法,而實際上完整的格式應該是list.foreach((element:Int) => println(element + 1))
我們沒有理由去寫這麼完整的東西,除非編譯器無法確定這個方法參數的類型了。
還有更加簡潔的方式,預留位置寫法:
用這個例子:val evenList = list.filter(element => element % 2 == 0)
這個例子中只有一個參數,且在方法體中出現一次,那麼可以簡寫為預留位置:
val evenList = list.filter(_ % 2 == 0),就是說,對每一個元素都用_替代。
還可以這樣子呢: val threeParamAdd = (_:Int) + (_:Int) + (_:Int)
這也是每一個參數在方法體中出現一次,第一個參數替代第一個_,第二個替代第二個_,以此類推。。。
我們盡量去省略參數類型,知道編譯器不能為我們判別出來我們的參數類型。
Partially applied functions(部分(不完全)應用(提供)函數):
list.foreach(x => println(x)) 改寫為 list.foreach(println _)
_不僅可以代表單獨的一個參數,還可以代表全部的參數。
當我們沒有給一個方法提供足夠的參數,而只是在方法名後面緊跟一個空格加上一個底線,如println _
def println(x: Any) = Console.println(x)
是Predef類中的方法,我們只提供一個println _,方法這個_代表的是這個方法中的所有的參數。
def sum(a: Int, b: Int, c: Int) = a + b + c
如果我們把他給一個變數時,可以這麼寫
val threeParamAdd = sum _ 部分應用函數
或者
val threeParamAdd = (_:Int) + (_:Int) + (_:Int) 預留位置寫法
或者
val threeParamAdd = (a:Int, b:Int, c:Int) => a + b + c 普通寫法
回過來說val threeParamAdd = sum _, 當對這個threeParamAdd進行調用的時候,如threeParamAdd(1, 2, 3),編譯器會看到sum _,然後他知道去找sum函數,發現有三個參數,依次替代,完成計算。
還會有另外的情況,如:
val oneAddSomeAddThree = sum(1, _:Int, 3),這樣的話,我們的oneAddSomeAddThree只允許有一個參數了。
如果某個地方整好需要一個函數作為參數,且這個函數可以使用部分應用函數的話,這個時候底線也可以省略。
如:list.foreach(println _) 可以修改成 list.foreach(println)
因為編譯器知道我們寫的這個println是一個函數,而不是別的東西,他會把他認為是一個部分應用函數。
5 閉包
引用了自由變數的函數,與這個自由變數一起組成的實體,叫做一個閉包
自由變數,除了函數的參數,以及函數中聲明的變數以外的變數。
綁定變數,函數的參數,以及其他函數編譯時間能夠確定的變數。
封閉術語:closed term,不包含自由變數的函數文本。
開放術語:open term,包含了自由變數的函數文本。
6 特殊的調用函數的形式
1 重複的參數:
def echo(arr: String*) { arr.foreach(println _) }
arr:String*, 是scala的文法,跟java中的String ... some,相似。
其中*指明這個參數是一個數組,數組元素的類型是String,數組長度為調用方法時給定的字串的個數。
如果現在有一個數組,想用以上方法調用,如:
val some = Array("This‘s", "little", "wonderful"),如果直接調用的話,會出錯,echo(some)
這時需要這樣傳遞參數:echo(some: _*)
編譯器會把some中的每一個元素作為參數傳遞給方法。
2 命名的參數:
def speed(distance:Float, time:Int) = distance / time
speed(100, 10)
speed(distance = 100, time = 10)
speed(time = 10, distance = 100)
3 具有預設值的參數
def nameBreaker(name:String = "Voctrals Lou", breaker:String = " ") = name.split(breaker)
如果我們調用nameBreaker(),返回Array("Voctrals", "Lout")
如果我們提供了其中的值,那麼預設值就不再起作用了,如
nameBreaker(breaker = "_"),傳回值為Array("Voctrals Lou")