Scala学习笔记(三)----高阶函数&集合

值函数

def add(x : Int, y: Int) =x+y
val _add = add _
val valFunName = funName _

嵌套函数

def里套def,功能上类似于java中的private方法。

def add3(x : Int , y : Int , z : Int) : Int = {
    def add2(x : Int , y : Int ) : Int = {
      x+y
    }
    add2(add2(x,y),z)
  }

内层函数可以访问外层函数里的变量。

匿名函数

  • (参数名:参数类型) => 表达式
  • 可将匿名函数赋值给var或val。
val fun = (x:Int) =>x+2
fun(10)
val l = List(1,2,3,4)
l.map((x:Int) => x*2)
l.map(2 * _ )//如果map只有一个参数,scala可以用一个下划线来代替这个参数

def a = (x:Int) => 2*x
var b = (x:Int) => 2*x
l.map(a)
l.map(b)

(x:Int,y:Int) => x+y

函数作为参数

scala中允许函数作为参数传给其它函数,在java中,只能通过给函数传递类对象,然后调用该类的方法来实现这种功能,而在c++中,则是用函数指针来完成的。 一个接收函数作为参数的函数,称为高阶函数。

  • fun(函数参数)
def fun(f:(Int,Int) => Int) :Int = f(1,2)
fun((x:Int,y:Int) => x+y)  //3
fun((x:Int,y:Int) => x-y)  //-1
fun((x:Int,y:Int) => x/y)  //0

def mulBy(factor : Double) = (x:Double) => factor * x
//后面的函数的参数来源于前面的函数的参数。
val aa = mulBy(2)
aa(20)  //40

闭包

闭包=代码+用到的非局部变量.

参数简化与类型推导

  • list.map((x:Int) => x+1)
  • list.map((x) => x+1)
  • list.map(x =>x+1)
  • list.map(_ + 1)

partial function

有的时候调用一个函数,我们只能拿到部分参数的值,而另外的参数暂时不能确定。

def add(x:Int,y:Int,z:Int) = x+y+z
val a = add(1,2,3)
val b = add(1,2,_ :Int)//此时b是一个偏函数了
b(10)//13

def _add = add(_:Int,_:Int,0)//此时_add也是一个偏函数
_add(1,2)//3

Currying

即把参数分开来写,此时可以用于偏函数。

def add(x:Int)(y:Int) = x+y
add(1)_//此时是一个偏函数

重要的高阶函数

  • map
  • filter
  • reduce/reduceLeft/reduceRight
  • fold/foldLeft/foldRight
val a = Array(1,2,3,4,5,6,7,8)
a.map(2*_)
a.filter(_ > 4)
a.reduce((x:Int,y:Int) => x+y)
a.reduce(_ + _)//36
a.fold(0)(_+_)//类似于reduce,但是可以提供一个初始值

By-name parameter

by value vs by name。如果是传值则在调用函数的时候就会求出参数表达式的值。如果是传名字,只有在用到的时候才会求值,延迟加载。

def test(flag: Boolean){
    println(flag)
}
//by value调用的时候就立刻求值
def test(flag => Boolean){
    println(flag)
}
//by name延迟计算,用到的时候才会求值
test(3>1)

def fun(block : => Unit){
    block
}

fun(println("xxx"))
fun{println("xxxx")}

下面我们来讲scala的集合。注意,scala的集合操作与spark的操作是平行的。

Seq

  • Range(start : Int, end : Int, step : Int).
  • Range方法带有apply()。
  • step可为正或者负,但不能为0.
1 to 10   //包含10
1.until(10) //不包含10
Range(1,10,3)//左闭右开
Range(100,10,-1)

List

List()产生的是一个不可变list。 - List 由Nil或者head + tail构成。其中tail又是一个List,Nil会生成一个空的list。 - 可以用双冒号来在list的头插入新元素。格式是new-value :: list - 构建: List(1,2,3) 或者 1 :: 2 :: 3 :: Nil - 基于List的模式匹配 - 利用迭代或者递归来处理List

val a = Nil   //a为List()
val l = List(1,2,3,4)
l.head   //1
l.tail   //(2,3,4)

可变集合与不可变集合

不可变集合从来不改变,因此你可以安全的共享其引用。你所有的操作都会生成一个新的集合,而不影响原集合,相反,对于可变集合来说,你的一些操作会影响到被操作的集合。scala会优先采用不可变集合。虽然我们可以同时往可变集合和不可变集合中添加元素,但是发生的事情是不一样的,对于不可变集合来讲,是新产生了一个集合,而可变集合则不同,还是原来的集合。

  • scala.collection.mutable.Map
  • scala.collection.immutable.Map
  • scala.collection.mutable.List
  • scala.collection.immutables.List

如果我们想定义可变集合,可以通过下面的语句来简写可变的集合:

scala.collection.mutable.Map(1 -> 2)
//或者,更简单的方式是:
import scala.collection.mutable
mutable.Map(1->2)

ListBuffer生成可变列表

可以进行的操作有+=/++=/-=/--=,进行操作之后返回的是自己,而不是产生一个新的集合。可以用toList或者toArray来讲可变列表变成不可变集合。

val b = mutable.ListBuffer(1)
b +=2       //ListBuffer(1,2) 
b +=3,4,5       //ListBuffer(1,2,3,4,5) 
b ++= List(6,7,8)   //ListBuffer(1,2,3,4,5,6,7,8) 
val a = b.toList   //a是一个不可变列表了

immutable Set

注意所有的immutable集合是不能有+=这种操作符的,而只能是下面的操作符:

  • +/++
  • -/--
  • scala.collection.immutable.SortedSet,如果你想用排序集合,可以用SortedSet, scala会用TreeSet来帮你排序.
val s = Set(1,2,3)   //s是一个不可变set
val a = s+5          //a也是一个不可变set,是由s生成的新set
val b = s+(4,5,6)
val c = s++List(7,8,9)
scala.collection.immutable.SortedSet(2,1,3,4)//TreeSet(1,2,3,4)

mutable Set

  • +/++/-/--都会创造一个新的set,原set不变。
  • +=/++=/-=/--=则不会创造一个新的set,直接对原set进行修改。
  • scala.colletion.mutable.SortedSet(trait)
  • TreeSet & BitSet

集合的重要方法

  • map
  • foreach/take
  • filter
  • flatten/flatMap
  • reduce/fold reduceLeft/foldLeft or Right
  • sum/max/min/count
  • zip
val l = List(1,2,3,4,5,6,7,8)
l.map(_ * 2).filter(_ > 8).foreach(println(_))
l.take(4)  //List(1,2,3,4)拿出前4个元素
l.map(_ * 2).filter(_ > 8).reduce(_ + _)
l.map(_ * 2).filter(_ > 8).fold(0)(_ + _)
l.max
l.sum
l.count(_ > 4)

val l1 = List(1,2,3)
val l2 = List("a","b","c","d")
l1 zip l2  //List((1,a),(2,b),(3,c))

val list = List(List(1,2),List(3,4),List(5,6))
list.flatten  //List(1,2,3,4,5,6)
list.flatMap(_.map(2*_))//
list.map(_.map(2*_))//List(List(2,4),List(6,8),List(10,12))

List(1,2,3,4).reduce( - )的运行过程是((1-2)-3)-4,即,对第一个元素和第二个元素调用操作函数,将结果与第三个元素再进行操作函数调用,依次进行。如果是reduceRight(_ + _),则会遵循下面的计算过程:1-(2-(3-4)),也就是说,将最后两个元素做操作,然后将结果作为第二操作数与倒数第三个元素一起进行下一次操作,以此类推。

fold()是和reduce()类似的函数,只不过它提供了初始值,表示从这个初始值作为第一个参数开始进行计算。

关于zip函数,如果l1和l2元素个数不相同时,只会短列表的元素个数,长列表多余的元素会被忽略掉。

flatten函数可以将列表里面包含的列表打开来。 flatMap则是将list的list操作之后将内层list打开。

Comments !