1. 程式人生 > >scala筆記-函數語言程式設計(13)

scala筆記-函數語言程式設計(13)

將函式賦值給變數
// Scala中的函式是一等公民,可以獨立定義,獨立存在,而且可以直接將函式作為值賦值給變數
 // Scala的語法規定,將函式賦值給變數時,必須在函式後面加上空格和下劃線
 
 def sayHello(name: String) { println("Hello, " + name) }
 val sayHelloFunc = sayHello _
 sayHelloFunc("leo")
匿名函式
 // Scala中,函式也可以不需要命名,此時函式被稱為匿名函式。// 可以直接定義函式之後,將函式賦值給某個變數;也可以將直接定義的匿名函式傳入其他函式之中
 // Scala定義匿名函式的語法規則就是,(引數名: 引數型別) => 函式體
3 // 這種匿名函式的語法必須深刻理解和掌握,在spark的中有大量這樣的語法,如果沒有掌握,是看不懂spark原始碼的
 
 val sayHelloFunc = (name: String) => println("Hello, " + name)
高階函式
// Scala中,由於函式是一等公民,因此可以直接將某個函式傳入其他函式,作為引數。這個功能是極其強大的,也是Java這種面向物件的程式語言所不具備的。
// 接收其他函式作為引數的函式,也被稱作高階函式(higher-order function)
val sayHelloFunc = (name: String) => println("Hello, " + name)
def greeting(func: (String) => Unit, name: String) { func(name) }
greeting(sayHelloFunc, "leo")

Array(1, 2, 3, 4, 5).map((num: Int) => num * num)

// 高階函式的另外一個功能是將函式作為返回值
def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
val greetingFunc = getGreetingFunc("hello")
greetingFunc("leo")
高階函式的型別推斷
// 高階函式可以自動推斷出引數型別,而不需要寫明型別;而且對於只有一個引數的函式,還可以省去其小括號;如果僅有的一個引數在右側的函式體內只使用一次,則還可以將接收引數省略,並且將引數用_來替代
// 諸如3 * _的這種語法,必須掌握!!spark原始碼中大量使用了這種語法!

def greeting(func: (String) => Unit, name: String) { func(name) }
greeting((name: String) => println("Hello, " + name), "leo")
greeting((name) => println("Hello, " + name), "leo")
greeting(name => println("Hello, " + name), "leo")

def triple(func: (Int) => Int) = { func(3) }
triple(3 * _)
Scala的常用高階函式
// map: 對傳入的每個元素都進行對映,返回一個處理後的元素
Array(1, 2, 3, 4, 5).map(2 * _)

// foreach: 對傳入的每個元素都進行處理,但是沒有返回值
(1 to 9).map("*" * _).foreach(println _)

// filter: 對傳入的每個元素都進行條件判斷,如果對元素返回true,則保留該元素,否則過濾掉該元素
(1 to 20).filter(_ % 2 == 0)

// reduceLeft: 從左側元素開始,進行reduce操作,即先對元素1和元素2進行處理,然後將結果與元素3處理,再將結果與元素4處理,依次類推,即為reduce;reduce操作必須掌握!spark程式設計的重點!!!
// 下面這個操作就相當於1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9
(1 to 9).reduceLeft( _ * _)

// sortWith: 對元素進行兩兩相比,進行排序
Array(3, 2, 5, 4, 10, 1).sortWith(_ < _)
閉包
// 閉包最簡潔的解釋:函式在變數不處於其有效作用域時,還能夠對變數進行訪問,即為閉包

def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
val greetingFuncHello = getGreetingFunc("hello")
val greetingFuncHi = getGreetingFunc("hi")

// 兩次呼叫getGreetingFunc函式,傳入不同的msg,並建立不同的函式返回
// 然而,msg只是一個區域性變數,卻在getGreetingFunc執行完之後,還可以繼續存在建立的函式之中;greetingFuncHello("leo"),呼叫時,值為"hello"的msg被保留在了函式體內部,可以反覆的使用
// 這種變數超出了其作用域,還可以使用的情況,即為閉包

// Scala通過為每個函式建立物件來實現閉包,實際上對於getGreetingFunc函式建立的函式,msg是作為函式物件的變數存在的,因此每個函式才可以擁有不同的msg

// Scala編譯器會確保上述閉包機制
SAM轉換
// 在Java中,不支援直接將函式傳入一個方法作為引數,通常來說,唯一的辦法就是定義一個實現了某個介面的類的例項物件,該物件只有一個方法;而這些介面都只有單個的抽象方法,也就是single abstract method,簡稱為SAM

// 由於Scala是可以呼叫Java的程式碼的,因此當我們呼叫Java的某個方法時,可能就不得不建立SAM傳遞給方法,非常麻煩;但是Scala又是支援直接傳遞函式的。此時就可以使用Scala提供的,在呼叫Java方法時,使用的功能,SAM轉換,即將SAM轉換為Scala函式

// 要使用SAM轉換,需要使用Scala提供的特性,隱式轉換


import javax.swing._
import java.awt.event._

val button = new JButton("Click")
button.addActionListener(new ActionListener {
  override def actionPerformed(event: ActionEvent) {
    println("Click Me!!!")
  }
})

implicit def getActionListener(actionProcessFunc: (ActionEvent) => Unit) = new ActionListener {
  override def actionPerformed(event: ActionEvent) {
    actionProcessFunc(event)
  }
}
button.addActionListener((event: ActionEvent) => println("Click Me!!!"))

package com.rttx.learn.Scala.scala1

import java.awt.event.{ActionEvent, ActionListener}

import javax.swing.{JButton, JFrame}

/**
  * @Author: king
  * @Datetime: 2018/11/2 
  * @Desc: TODO
  *
  */
object Sam {

  /**
    * 在Scala中,要某個函式做某件事時,會傳一個函式引數給它。
    * 而在Java中,並不支援傳送引數。通常Java的實現方式是將動作放在一個實現某介面的類中,
    * 然後將該類的一個例項傳遞給另一個方法。很多時候,這些介面只有單個抽象方法(single abstract method),
    * 在Java中被稱為SAM型別。
    * @param args
    */
  def main(args: Array[String]): Unit = {
    var count = 0


    val frame = new JFrame("SAM Testing")
    val button = new JButton("Increment")
    //java方式
    //    button.addActionListener(new ActionListener {
    //      override def actionPerformed(event: ActionEvent) {
    //        count += 1
    //        println(count)
    //      }
    //    })

    /**
      * scala方式: 隱式轉換,將一種型別自動轉換成另外一種型別,是個函式。
      * 因為在Scala中,函式是頭等公民,所以隱式轉換的作用也大大放大了。
      * 將這個函式和介面程式碼放在一起,就可以在所有預期ActionListener物件的地方,傳入(ActionEvent)=>Unit函式引數。
      * @param action
      * @return
      */
    implicit def makeAction(action: (ActionEvent) => Unit) =
      new ActionListener {
        override def actionPerformed(event: ActionEvent) { action(event) }
      }

    button.addActionListener((event: ActionEvent) => {count += 1; println(count)})

    frame.setContentPane(button)
    frame.pack()
    frame.setVisible(true)
  }
}

Currying函式
// Curring函式,指的是,將原來接收兩個引數的一個函式,轉換為兩個函式,第一個函式接收原先的第一個引數,然後返回接收原先第二個引數的第二個函式。
// 在函式呼叫的過程中,就變為了兩個函式連續呼叫的形式
// 在Spark的原始碼中,也有體現,所以對()()這種形式的Curring函式,必須掌握!

def sum(a: Int, b: Int) = a + b
sum(1, 1)

def sum2(a: Int) = (b: Int) => a + b
sum2(1)(1)

def sum3(a: Int)(b: Int) = a + b
object Curring {
  def main(args: Array[String]): Unit = {
    //Curring函式,指的是將原來接收兩個引數的一個函式,轉換為兩個函式,第一個函式接收原來的第一個引數,然後返回接收原來的第二個引數的第二個函式。
    //在函式呼叫的過程中,就變為了兩個函式連續呼叫的形式
    def sum(a:Int,b:Int)= a + b
    sum(1,1)
    //這就是顆粒函式
    def sum2(a:Int)=(b:Int)=>a+b
    sum2(1)(1)
    //顆粒函式的簡單寫法
    def sum3(a:Int)(b:Int)=a+b
    println(sum3(1)(3))
  }
}
return
// Scala中,不需要使用return來返回函式的值,函式最後一行語句的值,就是函式的返回值。在Scala中,return用於在匿名函式中返回值給包含匿名函式的帶名函式,並作為帶名函式的返回值。
// 使用return的匿名函式,是必須給出返回型別的,否則無法通過編譯

def greeting(name: String) = {
  def sayHello(name: String):String = {
    return "Hello, " + name
  }
  sayHello(name)
}