Groovy 學習手冊(6)


9. 不可變特性

不可變特性和函數式編程在一起就像是花生醬和果醬在一起一樣。雖然沒有必要非要在一起使用,但他們相處得很好。

在純正的函數式語言中,每個函數對本身之外沒有影響,即沒有副作用。這意味着每次調用函數時,返回相同輸入的相同值。

為了適應這種行為,需要不可變的數據結構。不可變的數據結構不能直接更改,但每次操作都返回一個新的數據結構。
例如,Scala語言里的 Map 就是不可變的。

val map =  Map("Smaug" -> "deadly")
val  map2 =  map + ("Norbert" -> "cute")
println(map2) // Map(Smaug -> deadly, Norbert -> cute)

所以,在上面的代碼中,map是保持不變的。
每個語言里都會提供一個關鍵字來定義一個變量或值是不可變的,在 Java 中使用 final 關鍵字來表示,Groovy 里面也是一樣。

public class Centaur {
    final String name
    public  Centaur(name) {this.name=name}
}
Centaur c = new  Centaur("Bane");
println(c.name) // Bane
c.name = "Firenze" //error

除此而外,Groovy 還提供了 @Immutable 注解來標注一個類為不可變類。它也提供了默認的構造方法,還有帶有屬性的構造方法,hashCode 方法,equals 方法和 toString 方法。下下面的例子。

import groovy.transform.Immutable
@Immutable
public class Dragon  {
    String name
    int scales
}
Dragon smaug = new  Dragon('Smaug', 499)
println smaug
// Output: Dragon(Smaug, 499)

10. Groovy Fluent GDK

在 Groovy 中,findAll和其他方法可以用在任何對象上,但尤其用在 List,Set,Range 上更加好用。除了findAllcollectinject還有以下的方法都可以在 Groovy 中使用。

  • each:根據給定的閉包條件遍歷所有的值;
  • eachWithIndex:在遍歷值時帶有兩個參數,一是具體的值,二是值對應的索引;
  • find:根據匹配的閉包條件找到滿足條件的第一個元素;
  • findIndexOf:根據匹配的閉包條件找到滿足條件的第一個元素,並返回對應的索引。

例如,collect方法用在對 List 的元素操作上非常簡單。

def  list = ['foo','bar']
def newList = []
list.collect( newList ) { it.substring(1) }
println newList //  [oo, ar]

在另一個例子里,

def dragons = [new Dragon('Smaug', 499), new Dragon('Norbert', 488)]
String longestName = dragons.
    findAll { it.name != null }.
    collect { it.name }.
    inject("") { n1, n2 -> n1.length() > n2.length() ? n1 : n2 }

上面的代碼首先查找所有不為 null 的名字,收集對象元素里的的名字屬性,然后再使用聚合方法找到所有名字里最長的那個。

11. Groovy Curry

curry方法用來在給閉包里的參數預定義一個默認值。它可以接受多個參數,並且在你預想的情況下從左到右逐一替換。請看下面的代碼:

def  concat = { x, y -> return  x + y }
// closure
def  burn = concat.curry("burn")
def inate = concat.curry("inate")

因為你只提供了第一個參數,它們的閉包會准備已經給定的字符串,burn方法准備了“burn”字符串,而inate方法准備了“inate”字符串。
burn(" wood") // == burn wood
你還可以使用composition閉包應用於兩個方法和一個輸入參數。

def composition = { f, g, x -> return f(g(x)) }
def burninate = composition.curry(burn, inate)
def  trogdor = burninate(' all the people')
println "Trogdor: ${trogdor}"
// Trogdor: burninate all the people

函數組合是函數編程中的一個重要思想。它允許你組合多個功能在一起創建復雜的算法。

12. 方法句柄

方法句柄允許你引用實際方法,就像它們是閉包一樣。當你想使用現有的方法,而不是閉包,或者你只是想一個替代的閉包語法時,這是非常有用的。例如,給定一個方法:

 def breathFire(name) { println "Burninating $name!" }

在定義了breathFire方法的類里面可以做如下操作:

 ['the country side', 'all the people'].each(this.&breathFire)

這樣就會把breathFire方法作為一個閉包傳遞給 each 方法,然后會打印如下結果:

Burninating the country side
Burninating all the people

13. 尾遞歸

在 Groovy 1.8版本中,trampoline方法被用來引入到閉包中用於對尾遞歸的優化。這允許閉包被順序調用而不是內存堆積,從而避免棧溢出和提高性能。
從Groovy 2.3 版本以后,可以使用trampoline方法來解決遞歸,甚至更好的辦法是使用注解 @TailRecursive, 例如,

import groovy.transform.*
@TailRecursive
long  totalPopulation(list, total = 0) {
  if (list.size() == 0)
    total
  else
    totalPopulation(list.tail(), total + list.first().population)
}

在 Groovy 的 List中,tail 方法會返回不包含第一個元素的 List,而first方法則返回 List 的第一個元素。直接上代碼:

@Canonical class City {int population}
def cities = (10..1000).collect{new City(it)}
totalPopulation(cities)
// 500455


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM