在Scala中定义函数时,需要定义函数的函数名,参数,函数体。
先写一个函数如下所示:
-- 单行的函数 : def sayHello(name:String) =print("Hello,"+name)
如果函数体中有多行代码,则可以使用代码块的方式包裹多行代码,代码块中最后一行的返回值就是整个函数的返回值。与java中不同,不是使用return返回值的。注意:一定要有等号,才会有返回值。
比如如下函数: 实现累加的功能。
def sum(n:Int)={
var sum=0
for(i <- 1 to n) sum+=i
sum // 函数的返回值
}
-- 递归函数 : 如果在函数体内递归调用函数自身,则必须手动给出函数的返回类型。
eg :实现经典的斐波那契数列:
-- 函数的默认参数
在Scala中,有时候我们调用某些函数时,不希望给出参数的具体值,而希望使用参数自身默认的值, 此时就可以在定义函数时使用默认参数。如果给出的参数不够,则会从左往右依次应用参数,如果调用函数时,给出了参数,就是用实际值代替默认参数。
-- 带名参数
在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。还可以混合使用未命名参数和带名参数,但是未命名参数必须排在带名参数的前面。
-- 变长参数 : 有时我们需要将函数定义为参数个数可变的形式,此时可以使用变长参数定义函数
-- 使用序列调用变长参数
如果想要将一个 已有的序列直接调用变长参数函数,是不对的,比如,val s=sum(1 to 5) , 此时需要使用Scala特殊的语法将参数定义为序列,让Scala解释器能够识别,这种语法非常有用!一定要好好注意,在spark的源码中大量地使用到了。
eg: val s=sum(1 to 5 :_*)
// 案例: 使用递归函数实现累加
def sum2(nums:Int*):Int ={
if(nums.length ==0) 0
else nums.head +sum2(nums.tail:_*)
}
-- 过程 : 定义函数时,如果函数体直接包裹在了花括号里面,而没有使用=连接,则函数的返回值类型就是Unit ,这样的函数就被称之为
过程。过程通常用于不需要返回值的函数。 过程还有一种写法,就是将函数的返回值类型定义为Unit
def sayHello(name:String)="Hello," +name
def sayHello(name:String) {print("Hello, "+name);"Hello, "+ name}
def sayHello(name :String):Unit="Hello, "+name
-- lazy值 : 在Scala中,提供了lazy值得特性,如果将一个变量声明为lazy,则只有第一次使用该变量的时候,变量对应的表达式才会发生计算。这种特性对于特别耗时的计算操作特别有用,比如打开文件进行IO,进行网络IO等。
import scala.io.Source._
lazy val lines=fromFile("C://Users//Administrator//Desktop//test.txt").mkString
//,即使文件不存在,也不会报错,只有第一个使用变量时会报错,证明了表达式计算的lazy特性。
-- 异常:在Scala中,异常处理和捕获机制与Java是非常相似的。
try{
throw new IllegalArgumentException("x should not be negative")
} catch{
case _:IllegalArgumentException =>println("Illegal Argument") //
}finally{
print("release resources!")
}
--
数组操作之Array、ArrayBuffer以及遍历数组
在Scala中,Array代表的含义与java类似 ,也是长度不可改变的数组。此外,由于Scala与Java都是运行在JVM中,双方可以相互调用,因此Scala数组的底层实际上是Java数组。例如字符串数组在底层就是Java的String[],整数数组在底层就是Java的Int[]
数组初始化之后,长度就固定下来了,而且元素全部根据其类型初始化
val a=new Array[Int](10)
a[0] =1 // 赋值
val a=new Array[String](10)
可以直接使用Array()创建数组,元素类型自动推断
val a =Array("hello","World")
a[0] ="hi"
-- ArrayBuffer : 在Scala中,如果需要类似于java中的ArrayList这种长度可变的集合类,则可以使用ArrayBuffer
// 如果不想每次都使用全限定名,则可以预先导入ArrayBuffer类,
import scala.collection.mutable.ArrayBuffer
// 使用ArrayBuffer()的方式可以创建一个空的 ArrayBuffer
val b =ArrayBuffer[Int]()
// 使用 += 操作符 ,可以添加一个元素,或者多个元素
b+= 1 b+=(2,3,4,5)
// 使用 ++=操作符 ,可以添加其他集合中的所有元素
b++= Array(6,7,8,9,10)
// 使用trimEnd()函数,可以从尾部截断指定个数的元素
b.trimEnd(5)
// 使用insert()函数可以在指定的位置上插入元素(效率太低,因为要移动指定位置后的所有元素)
b.insert(5,6) //在index为5的位置上插入 元素6
b.insert(6,7,8,9,10) // 插入多个元素
// 使用remove() 函数可以移除指定位置的元素
b.remove(1) b.remove(1,3)
// Array 与ArrayBuffer可以相互进行转换
b.toArray
b.toBuffer
相关代码展示如下:
-- 遍历Array和ArrayBuffer
// 使用for循环 和until遍历Array/ArrayBuffer
// until是RichInt提供的函数
for(i <- 0 until b.length) print(b(i) + " ")
// 跳跃遍历Array/ArrayBuffer
for(i <- 0 until(b.length,2)) print(b(i) +" ")
// 从尾部遍历 Array /ArrayBuffer
for(i <- (0 until b.length).reverse) print(b(i)+" ")
// 使用 增强型for循环, 遍历 Array/ArrayBuffer
for(e <- b) print(e+ " ")
-- 数组常见操作
val a =Array(9,5,7,8,3)
// 数组元素求和
val sum =a.sum
//获取数组最大值
val max =a.max
// 对数组进行排序
scala.util.Sorting.quickSort(a)
// 获取数组中所有元素内容
a.mkString a.mkString(",") a.mkString("<",",",">")
// toString函数
a.toString() b.toString()
代码展示如下所示:
-- 使用yield 和函数式编程对数组进行转换
val c=Array(1,2,3,4,5)
// 对Array进行转换, 获取的还是Array
val c2= for( ele <- c2) yield ele *ele
val d=ArrayBuffer[Int]()
d+=(1,2,3,4,5)
val d2= for(ele <- d)yield ele * ele
//结合if守卫 ,仅转换需要的元素
val c3 =for(ele <- c if ele %2 ==0)yield ele *ele
// 使用函数式编程转换数组(通常使用第一种方式)
c.filter(_ % 2 ==0).map(_ * 2) // 筛选c中的偶数,并进行乘2操作
c.filter{_ % 2==0}.map{}
相关代码展示如下:
-------- 算法案例 :移除第一个负数之后的所有负数 ------
// 构建数组
val a=ArrayBuffer[Int]()
a+=(1,2,3,4,5,-1,-5,-3,-9)
// 每发现一个第一个负数之后的负数,就进行移除,性能较差,多次移动数组
var foundFirstNegative=false;
var arrayLength=a.length ;
var index= 0
while(index < arrayLength){
if(a(index) >= 0)
index +=1
else {
if(!foundFirstNegative){foundFirstNegative=true;index+=1}
else {a.remove(index);arrayLength -=1}
}
由于性能较差,对其进行改良,改良版如下:
// 重新构建数组
val a=ArrayBuffer[Int]()
a += (1,2,3,4,5,-1,-3,-5,-9)
// 每记录所有不需要移除的元素的索引,稍后一次性移除所有需要移除的元素
// 性能较高,数组内的元素迁移只要执行一次即可。
var foundFirstNegative=false;
val keepIndexes=for(i <- until a.length if!foundFirstNegative || a(i) >=0 )yield{
if(a(i) < 0) foundFirstNegative=true
i
}
for(i <- 0 until keepIndexes.length ){a(i)=a(keepIndexes(i))}
a.trimEnd(a.length-keepIndexes.length)
-- Map
// 创建一个不可变的Map
val ages=Map("Leo" -> 30,"Jen" ->25,"Jack" ->23)
ages("Leo")=31
// 创建一个可变的Map
val ages=scala.collection.mutable.Map("Leo" -> 30,"Jen" ->25,"Jack" ->23)
ages("Leo") = 31
// 使用另外一种方式定义Map元素
val ages =Map(("Leo",30),("Jack",23),("Jen",25))
// 创建一个空的HashMap
val ages = new scala.collection.mutable.HashMap[String,Int]
相关代码展示如下:
创建一个空的HashMap:
-- 访问Map的元素
// 获取指定key对应的value ,如果不存在,会报错
val leoAge=ages("Leo")
val leoAge=ages("leo")
// 使用contains函数检查key是否存在
val leoAge=if(ages.contains("leo")) ages("leo") else 0
// getOrElse 函数
val leoAge=ages.getOrElse("leo",0) // 如果存在就返回,不存在就返回 0
代码展示如下:
-- 修改 Map的元素
//更新Map的元素
ages("Leo")=31
// 增加多个元素
ages+=("Mike"->35,"Tom"->40)
//移除元素
ages-="Mike"
//更新不可变的map
val ages2=ages+("Mike"->36,"Tom"->41)
// 移除不可变map的元素
val ages3=ages -"Tom"
// 可以看出,此对不可更新的map的操作其实就是重新生成一个map ,而并不是改变原来的map
代码展示如下:
-- 遍历Map
// 遍历map的entrySet
for((key,value)<- ages)println(key+" "+value)
// 遍历map的key
for(key <- ages.keySet)println(key)
// 遍历map的value
for(value <-ages.values) println(value)
// 生成新map ,反转key和value
for((key,value) <- ages) yield (value,key)
代码展示如下:
-- SortedMap 和LinkedHashMap
//SortedMap可以自动对map的key进行排序
val ages=scala.collection.immutable.SortedMap("leo"->30,"alice"->15,"jen"->25)
// LinkedHashMap可以记住插入entry的顺序
val ages=new scala.collection.mutable.LinkedHashMap[String,Int]
ages("leo") =30; ages("alice")=15 ;ages("jen")=25
// 除了LinkedHashMap,别的map不会对插入顺序进行维护,而前者,在插入时,会有一个链表来维护记录插入顺序
代码展示如下:
-- Map的元素类型-Tuple
// 简单Tuple
val t=("leo",30)
// 访问Tuple
t._1
// zip操作(拉链操作,按照一对一对的进行拼接)
val names=Array("leo","jack","mike")
val ages=Array(30,24,26)
val nameAges=names.zip(ages)
for((name,age) <- nameAges)println(name+" : "+ age)
代码展示如下: