scala函数经过def关键字定义,def前面能够具备修饰符,能够经过private、protected来控制其访问权限。es6
注意:没有public,不写默认就是public的。此外也可跟上override,final等关键字修饰。数组
[private/protected] def 函数名(参数列表):返回值声明 = {函数体}闭包
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}
能够为函数的参数设置默认值。spa
示例:
//默认参数的使用 def f8(a:String,b:String="[",c:String="]")={ b+a+c }
占位符:占位符指的是scala中的下划线_ ,能够用它看成一个或多个参数来使用。
使用_占位符的前提要求:每一个参数在函数仅出现一次。
使用下划线时,若是类型能够自动推断出,则不用声明类型。若是没法自动推断类型,则在下划线后本身来显示声明类型便可。
示例:
//要求经过reduceLeft函数计算阶乘结果 val a2=Array(1,2,3,4) a2.reduceLeft{(a:Int,b:Int)=>{a*b}} a2.reduceLeft{_*_}
Scala中的函数分为成员函数、本地函数(内嵌在函数内的函数)、函数值(匿名函数)、高阶函数。
成员函数:函数被使用在类的内部,做为类的一份子,称为类的成员函数。
示例:
class Person { //eat方法是Person的成员方法 def eat() { println("eat") } }
本地函数:函数内嵌的函数称为本地函数,这样的函数外界没法访问。
示例:
class Person { //eat方法是Person的成员方法 def eat() { println("eat") //本地函数:内嵌在函数内的函数。对象不能直接调用本地函数。 def cook() { println("cook") } } }
函数值(匿名函数):
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)}
高阶函数:函数能够做为方法的参数进行传递和调用。
示例:
//定义一个高阶函数,能够将函数看成参数传递 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*)={}
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)(_+_)
柯里化技术在提升适用性、延迟执行或者固定易变因素等方面有着重要重要的做用,加上scala语言自己就是推崇简洁编码,使得一样功能的函数在定义与转换的时候会更加灵活多样。另外在Spark的源码中有大量运用scala柯里化技术的状况,须要掌握好该技术才能看得懂相关的源代码。
在scala柯里化中,闭包也发挥着重要的做用。所谓的闭包就是变量出了函数的定义域外在其余代码块还能其做用,这样的状况称之为闭包。就上述讨论的案例而言,若是没有闭包做用,那么转换后函数其实返回的匿名函数是没法在与第一个参数a相关结合的,天然也就没法保证其所实现的功能是跟原来一致的。
适用于全部集合。
拆分,将一个集合按一个布尔值分红两个集合,知足条件的一个集合,其余另一个集合。
按照指定原则拆分,返回的是一个二元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))
映射,把一个集合转换为另一个集合。集合中元素个数不变。
改变集合类型中,元素的形式或数据,返回一个新的集合。此方法不会改变集合中元素的个数,只是改变了数值和形式。
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))
扁平化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))
过滤。
val l4=List(1,2,3,4,5) l4.filter(x=>x>3) //> res5: List[Int] = List(4, 5)
归约,reduce的过程:将上次的运行结果和下一个值进行运算。
函数接收两个参数 => 返回一个值。
等价于reduceLeft。
l4.reduce{_+_} //> res6: Int = 15
按指定规则作聚合,最后将结果返回到一个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)))
此方法是针对的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)
排序。
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中的集合类型