scala(一)方法&函数

写在前面

众所周知,scala一向宣称自己是面向函数的编程,(java表示不服,我是面向bean的编程!)那什么是函数?

在接触java的时候,有时候用函数来称呼某个method(实在找不出词了),有时候用方法来称呼某个method,虽然method的中文翻译就是“方法”,但对于java来说,方法和函数是等价的,或者说没有函数这个概念。

而对于scala,这两者似乎有一个较为明确的边界。

你会发现满世界的函数,而你却在写方法

Scala 方法&函数

方法

Scala的方法和java可以看成是一样的,只是多了点语法糖。

比如无参方法在申明时可以不加括号,甚至在调用过程也不用加括号

def f = 1+1
println(f)

比如方法可以添加泛型规则,这在java中只能在类申明

def f[T](t: T) = {t}

还有其它很多细节语法,遇到才深入吧

一般而言只要知道函数的结构就行(但是我想说,spark的代码就没有一个函数长成这样的啊..),请忽略下图的“函数”字样,其实就是方法

 

方法应用

def method(): Unit ={
  //本地方法
  def print(str:String): Unit ={
    println(str)
  }
  print("hello")
}

方法的语法还是跟java差不多的,只是有些可以省略而已。

比较重要的就是本地方法,即方法中嵌套方法

函数

Scala的函数是基于Function家族,0-22,一共23个Function Trait可以被使用,数字代表了Funtcion的入参个数

函数语法

下面这四个函数的意义是一样的

// println(fun1)
// println(fun2)
// println(fun3)
// println(fun4)
// 都为<function2>
val fun1 = new Function2[Int,Int,Int]() {
  override def apply(v1: Int, v2: Int): Int = {
    v1+v2
  }
}

val fun2 = new ((Int, Int) => Int)() {
  override def apply(v1: Int, v2: Int): Int = {
    v1+v2
  }
}

val fun3 = (v1:Int,v2:Int) => v1+v2

// _可以把method转换成function
val fun4 = fun4Method _
def fun4Method(v1:Int,v2:Int): Int = {
  v1+v2
}

一般我们都采用第三种fun3定义方式,也是最难懂的一个定义方式。具体结构参考下图

 

那函数有什么用呢?

Java里只有方法都能适应一切需求,那scala又提出函数的概念肯定有意义。

1.函数可以直接赋值给变量,可以让函数很方便的传递

2.闭包(closure),可以把灵活操作代码块,从而引申出其他灵活的语法

函数应用

在spark中,有很多方法入参中使用函数的场景,比如如下函数

defrunJob[T,U](fun: Iterator[T] => U ,resHandler: (Int, U) => Unit): Unit ={
  //忽略里面的逻辑
}

其中的fun和resHandler都是函数

Fun是入参为Iterator[T],返回值为U的函数,一个入参的函数其实就是Function1的实例

resHandler是入参为Int和 U无返回值的函数,二个入参的函数其实就是Function2

模拟spark中常见的一段代码语法,拿一个普通scala类型的例子来说

//模拟spark的runJob方法
def runJob[T,U](fun: Iterator[T] => U ,resHandler: (Int, U) => Unit): Unit ={
  val listBuffer = new ListBuffer[T]
  listBuffer.append("h".asInstanceOf[T])
  listBuffer.append("e".asInstanceOf[T])
  listBuffer.append("l".asInstanceOf[T])
  listBuffer.append("l".asInstanceOf[T])
  listBuffer.append("o".asInstanceOf[T])
  //这里调用函数其实用到了伴生对象的概念,fun(xxx)就是fun.apply(xxx)
  val res = fun(listBuffer.iterator)
  //spark中,这里是每个partition的数据都存入arr,这里做模拟就一个partition了:)
  resHandler(0,res)
}
 
//模拟调用runJob的方法
def main(args: Array[String]): Unit = {
  val arr = new Array[String](1)
  //fun函数的实际逻辑
  val fun = (it:Iterator[String]) => {
    val sb = new StringBuilder()
    while (it.hasNext)
      sb.append(it.next())
    sb.toString()
  }

  //resHandler函数的实际逻辑
  val resHandler = (i:Int,res:String) => arr(i) = res
  runJob[String,String](fun ,resHandler)
  println(arr.mkString(""))
}

其实就是传递函数的逻辑,和java的匿名类差不多(只有一个方法的匿名类),只是多了点语法糖

这么做的好处也是不言而喻的

1.可以构造出更抽象的方法,使得代码结构更简洁

2.spark的思想就是lazy,而函数传递也是一个lazy的过程,只有在实际触发才会执行

偏函数

英文为PartialFunction,不知道这么翻译对不对,貌似都这么叫。

PartialFunction其实是Funtion1的子类

参考源码

trait PartialFunction[-A, +B] extends (A => B)

A => B就是标准的函数结构

那PartialFunction有什么作用呢?

模式匹配!

PartialFunction最重要的两个方法,一个是实际的操作逻辑,一个是校验,其实就是用来做模式匹配的。

参考资料

《Scala编程》

原文地址:https://www.cnblogs.com/ulysses-you/p/7551188.html