Scala的函数

Scala的函数

一、函数的声明

    scala函数经过def关键字定义,def前面能够具备修饰符,能够经过private、protected来控制其访问权限。es6

    注意:没有public,不写默认就是public的。此外也可跟上override,final等关键字修饰。数组

1.格式

    [private/protected] def 函数名(参数列表):返回值声明 = {函数体}闭包

2.函数的返回值

    1)函数体中return关键字每每能够省略掉,一旦省略掉,函数将会返回整个函数体中最后一行表达式的值,这也要求整个函数体的最后一行必须是正确类型的值的表达式。ide

    2)大部分时候scala均可以经过=符号来自动推断出返回值的类型,因此一般返回值类型声明能够省略。函数

    可是注意:若是由于省略了返回值类型形成歧义,则必定要写上返回值声明。oop

    3)若是函数体只有一行内容,则包裹函数体的大括号能够省略。优化

    4)若是返回值类型是UNIT,则另外一种写法是能够去掉返回值类型和等号,把方法体写在花括号内,而这时方法内不管返回什么,返回值都是UNIT。至关于Java中的void。编码

    示例:es5

//方法的返回值为空
    private def f1(){}
	protected def f2():String={"hello"}
	def f3()={"hello"}
	//若是函数体只一行内容,能够省了花括号
	def f4()="hello"
	//定义方法参数类型,返回值类型,及返回值
	def f5(a:Int,b:Int)={a+b}

3.默认参数

    能够为函数的参数设置默认值。spa

    示例:

//默认参数的使用
	def f8(a:String,b:String="[",c:String="]")={
		b+a+c
	}

4.占位符

    占位符:占位符指的是scala中的下划线_ ,能够用它看成一个或多个参数来使用。

    使用_占位符的前提要求:每一个参数在函数仅出现一次。

    使用下划线时,若是类型能够自动推断出,则不用声明类型。若是没法自动推断类型,则在下划线后本身来显示声明类型便可。

示例:

//要求经过reduceLeft函数计算阶乘结果
val a2=Array(1,2,3,4)
a2.reduceLeft{(a:Int,b:Int)=>{a*b}}
a2.reduceLeft{_*_}

二、函数的种类

    Scala中的函数分为成员函数、本地函数(内嵌在函数内的函数)、函数值(匿名函数)、高阶函数。

1.成员函数

    成员函数:函数被使用在类的内部,做为类的一份子,称为类的成员函数。

    示例:

class Person {
  //eat方法是Person的成员方法
  def eat() {
    println("eat")
  }
}

2.本地函数

    本地函数:函数内嵌的函数称为本地函数,这样的函数外界没法访问。

    示例:

class Person {
  //eat方法是Person的成员方法
  def eat() {
    println("eat")
    //本地函数:内嵌在函数内的函数。对象不能直接调用本地函数。
    def cook() {
      println("cook")
    }
  }
}

3.匿名函数

    函数值(匿名函数):

    1.匿名函数没有函数名。

    2.匿名函数的做用是配合高阶函数来使用的,匿名函数能够做为函数的参数进行传递。

    示例:

(a:Int,b:Int)=>{a+b}
(a:Int,b:Int)=>a+b
val a1=Array(1,2,3,4)
//a=1	b=2	a+b=3
//a=3	b=3	a+b=6
//a=6	b=4 a+b=10
a1.reduceLeft{(a:Int,b:Int)=>a+b}
a1.reduceLeft{(a,b)=>a+b}
a1.foreach{(x:Int)=>{println(x)}}
a1.foreach{x=>println(x)}

4.高阶函数

    高阶函数:函数能够做为方法的参数进行传递和调用。

    示例:

//定义一个高阶函数,能够将函数看成参数传递
  def f2(a:Int,b:Int,f:(Int,Int)=>Int)={
  	f(a,b)
  }
  f2(2,3,(a:Int,b:Int)=>{a+b})
  f2(2,3,(a,b)=>a+b)
  f2(2,3,(a,b)=>a*b)
  f2(2,3,_*_)
  //定义一个高阶函数,要求:传入一个String类型的参数,以及一个处理String类型的匿名函数
  //此高阶函数的返回值就是匿名函数的返回值
  def f3(a:String,f:(String)=>Array[String])={
  	f(a)
  }
  //注意:匿名函数必定要和指定返回值类型匹配
  f3("hello,world",(a:String)=>{a.split(",")})
  f3("hello,world",a=>a.split(","))
  f3("hello,world",a=>a split ",")
  f3("hello,world",_.split(","))
  //要求经过reduceLeft函数计算阶乘结果
  val a2=Array(1,2,3,4)
  a2.reduceLeft{(a:Int,b:Int)=>{a*b}}

三、递归

    想要实现递归方法,有两个要素能够快速的实现。

    要素1:找出递归结束的条件。

    要素2:找出函数的映射关系。

    scala中,若是在递归时,保证函数体的最后一行为递归调用,则称这样的递归为尾递归。scala会针对尾递归作优化处理,因此建议在写递归时写成尾递归形式。

    范例:

    斐波那契数列:1 1 2 3 5 8 13 ?

    要素1:找出递归结束的条件:f(n)=f(n-1)+f(n-2)

    要素2:找出函数的映射关系:f(0)=1;f(1)=1

    因此代码就能够为:

def f1(n:Int):Int={
  	if(n==0)return 1
  	if(n==1)return 1
  	else f1(n-1)+f1(n-2)
}

    示例:

//从1开始作加法,只加偶数,当加和累计超过50时,结束递归
  //示意:2+4+6……
  def f1(num: Int, sum: Int): Int = {
    if (sum > 50) return sum;
    if (num % 2 == 0) { f1(num + 1, sum + num) }
    else { f1(num + 1, sum) }
  }

  //2 3 4 9 8 27 16 ?
  //f(n)= f(n-2)*2
  //f(n)=f(n-2)*3
  //f(0)=2;f(1)=3
  def f2(n: Int): Int = {
    if (n == 0) return 2
    if (n == 1) return 3
    if (n % 2 == 0) return f2(n - 2) * 2
    else f2(n - 2) * 3
  }

  //2 3 4 9 16 81 ?
  // n的取值:f(0) f(1) f(2)  f(3)  f(4) f(5)
  //当n为偶数时,f(n)=f(n-2)*f(n-2)
  //当n为奇数是,f(n)=f(n-2)*f(n-2)
  //fn=f(n-2)*f(n-2)
  def f3(n: Int): Int = {
    if (n == 0) return 2
    if (n == 1) return 3
    else f3(n - 2) * f3(n - 2)
  }

  //求 1~n的数字之和
  //1    3      6     10     15
  //f(0) f(1)  f(2)   f(3)   f(4)
  //f(n)=f(n-1)+n+1
  def f5(n: Int): Int = {
    if (n == 0) return 1
    else f5(n - 1) + n + 1
  }

  //给定一个初始值n,并设定sum的初始值为0,当求和sum>12时结束递归
  //0  1  3   6   10  15
  //f(0,0)——n=0,sum=0
  //f(1,1)——n=1,sum=1
  //f(2,3)——n=2,sum=3
  //f(3,6)——n=3,sum=6
  //f(n+1,sum+n)
  def f6(n: Int, sum: Int): Int = {
    if (sum > 12) return sum
    else f6(n + 1, sum + n)
  }

  //给定一个scope范围,计算0~scope范围的整数之和,
  //当和>12或者达到scope边界时,结束递归
  def f7(n: Int, sum: Int, scope: Int): Int = {
    if (sum > 12) return sum
    if (n - 1 == scope) return sum
    else f7(n + 1, sum + n, scope)
  }

四、变长参数

    在scala中,能够指明函数的最后一个参数是重复的。从而容许客户向函数传入可变参数的列表。

    想要标注一个重复参数,能够在参数的类型以后放一个星号。重复参数(可变参数)的类型是声明参数类型的数组。

    示例:

//定义变长参数
  def f1(num:Int*)={}
  def f2(num:Int*)={
  	for(i<-num)println(i)
  }
  f2(1,2,3,4,5)
  def f21(a:Int,str:String*)={}

五、柯里化 Currying

1.介绍

    scala的柯里化的做用是结合scala的高阶函数,从而容许用户自创建控制结构。

    柯里化(Currying)技术 Christopher Strachey 以逻辑学家 Haskell Curry 命名的(尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的)。它是把接受多个参数的函数变换成接受一个单一参数的函数,而且返回接受余下的参数且返回结果的新函数的技术。

案例

//首先咱们定义一个函数:
    def f1(a:Int,b:Int):Int={a+b}
    //如今咱们把这个函数变一下形:
    def f2(a:Int)(b:Int)={a+b}
    //那么咱们应用的时候,应该是这样用:f2(2)(3),最后结果都同样是5,这种方式(过程)就叫柯里化。
    val r1=f2(2)(3)
    //柯里化实质上会演变成这样一个函数:
    //接收一个参数a,返回一个匿名函数,
    //该匿名函数又接收一个参数b,函数体为a+b
    def f3(a:Int)=(b:Int)=>a+b
    val f4=f3(2)
    //请思考 f4(3)的值是多少?答案是:5
    f4(3)

示例

//柯里化
  def f3(a:Int,b:Int)={a+b}
  def f31(a:Int)(b:Int)={a+b}
  f3(2,3)
  f31(2)(3)

  def f4(a:Int,b:Int,c:Int)={a+b+c}
  def f41(a:Int)(b:Int)(c:Int)={a+b+c}
  def f42(a:Int,b:Int)(c:Int)={a+b+c}
  def f43(a:Int)(b:Int,c:Int)={a+b+c}

  def f5(a:Int,b:Int,f:(Int,Int)=>Int)={f(a,b)}
  def f51(a:Int)(b:Int,f:(Int,Int)=>Int)={f(a,b)}
  def f52(a:Int,b:Int)(f:(Int,Int)=>Int)={f(a,b)}
  def f53(a:Int)(b:Int)(f:(Int,Int)=>Int)={f(a,b)}
  f5(2,3,_+_)
  f52(2,3)(_+_)

4.做用

    柯里化技术在提升适用性、延迟执行或者固定易变因素等方面有着重要重要的做用,加上scala语言自己就是推崇简洁编码,使得一样功能的函数在定义与转换的时候会更加灵活多样。另外在Spark的源码中有大量运用scala柯里化技术的状况,须要掌握好该技术才能看得懂相关的源代码。

    在scala柯里化中,闭包也发挥着重要的做用。所谓的闭包就是变量出了函数的定义域外在其余代码块还能其做用,这样的状况称之为闭包。就上述讨论的案例而言,若是没有闭包做用,那么转换后函数其实返回的匿名函数是没法在与第一个参数a相关结合的,天然也就没法保证其所实现的功能是跟原来一致的。

六、内置高阶函数

    适用于全部集合。

1.partition

    拆分,将一个集合按一个布尔值分红两个集合,知足条件的一个集合,其余另一个集合。

    按照指定原则拆分,返回的是一个二元Tuple。

val l1=List(1,2,3,4,5,6)
  l1.partition{x=> x%2==0}
//> res0: (List[Int], List[Int]) = (List(2, 4, 6),List(1, 3, 5))

2.map

    映射,把一个集合转换为另一个集合。集合中元素个数不变。

    改变集合类型中,元素的形式或数据,返回一个新的集合。此方法不会改变集合中元素的个数,只是改变了数值和形式。

val l2=List("hadoop","world","hello","hello")
  l2.map(x=>(x,1))
//> res1: List[(String, Int)] = List((hadoop,1), (world,1), (hello,1), (hello,1))
  val l3=List("hello word","hello hadoop")
  l3.map{x=>x.split(" ")}
//> res2: List[Array[String]] = List(Array(hello, word), Array(hello, hadoop))

3.flatMap

    扁平化map,会取出集合的元素。注意,此方法会改变集合中的元素个数。

    通常引用场景:读取文件后,处理文件,将每行数据按指定分割符切分。

l3.flatMap{x=>x.split(" ")}
//> res3: List[String] = List(hello, word, hello, hadoop)
  //要求:操做l3将其变成(word,1)的形式
l3.flatMap{x=>x.split(" ")}.map(x=>(x,1))
//> res4: List[(String, Int)] = List((hello,1), (word,1), (hello,1), (hadoop,1))

4.filter

    过滤。

val l4=List(1,2,3,4,5)
  l4.filter(x=>x>3)
//> res5: List[Int] = List(4, 5)

5.reduce

    归约,reduce的过程:将上次的运行结果和下一个值进行运算。

    函数接收两个参数 => 返回一个值。

    等价于reduceLeft。

l4.reduce{_+_}
//> res6: Int = 15

6.groupBy

    按指定规则作聚合,最后将结果返回到一个map映射里。

    按照指定原则作分组,返回的是Map类型。Map的key是分组键,value是对应的List集合。

val l5=List(("bj",1),("sh",2),("bj",3),("sh",4),("sz",5))
l5.groupBy{x=>x._1}
//> res7: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(bj ->List((bj,1), (bj,3)), sz -> List((sz,5)), sh -> List((sh,2), (sh,4)))
l5.groupBy{case(addr,count)=>addr}
//> res8: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(bj ->List((bj,1), (bj,3)), sz -> List((sz,5)), sh -> List((sh,2), (sh,4)))

7.mapValues

    此方法是针对的Map类型的值作操做,此方法只适用于Map类型。

val m1=Map("rose"->23,"tom"->25,"jary"->30)
  m1.mapValues {x=>x+10}
//> res9: scala.collection.immutable.Map[String,Int] = Map(rose -> 33, tom -> 35, jary -> 40)

8.sortBy

    排序。

 

val l6=List((2,"aaa"),(1,"bbb"),(4,"ddd"),(3,"ccc"))
  l6.sortBy{x=>x._1}
//> res10: List[(Int, String)] = List((1,bbb), (2,aaa), (3,ccc), (4,ddd))
  l6.sortBy{x=>x._2}
//> res11: List[(Int, String)] = List((2,aaa), (1,bbb), (3,ccc), (4,ddd))
  l6.sortBy{case(num,str)=>num}
//> res12: List[(Int, String)] = List((1,bbb), (2,aaa), (3,ccc), (4,ddd))

案例:统计单词频率

    统计出每一个单词出现的频次。最后的结果形式:(hello,5)(hadoop,2)……

val l7=List("hello hadoop","hello world","hello spark","hello hadoop","hello hive")
//方法一:
  l7.flatMap{line=>line.split(" ")}.groupBy{word=>word}.mapValues { list => list.size }.foreach{println(_)}
//方法二:
  l7.flatMap { line => line.split(" ") }.map { word => (word,1) }.groupBy(x=>x._1).mapValues{list=>list.size}.foreach{println(_)}
//方法三:
  l7.flatMap { line => line.split(" ") }.map { word => (word,1) }.groupBy(x=>x._1).mapValues{list=>list.map(x=>x._2).reduce(_+_)}.foreach{println(_)}
//简化写法:
  l7.flatMap{_.split(" ")}.map{(_,1)}.groupBy(_._1).mapValues{_.map(_._2).reduce(_+_)}.foreach{println(_)}
//要求统计单词频次,而后返回频次最高的前2项结果
  l7.flatMap { line => line.split(" ") }.map { word => (word,1) }.groupBy(x=>x._1).mapValues{list=>list.map(x=>x._2).reduce(_+_)}.toList.sortBy(x=> -x._2).take(2)
//> res13: List[(String, Int)] = List((hello,5), (hadoop,2))

上一篇:Scala语法介绍

下一篇:Scala中的集合类型