函数是一等公民

可以使用def来定义一个函数。函数体是一个表达式。

匿名函数:(将匿名函数作为表达式,传给变量)

  val squareVal = (a: Int) => a * a

带函数参数的函数(高阶函数)

  def addOne(f: Int => Int, arg: Int) = f(arg) + 1

      f: Int => Int是函数参数,表示传进来一个函数,函数的传入类型是int,传出类型也是int

函数的传名参数:

1 var assertionsEnabled=true
2 def myAssert(predicate: () => Boolean ) =
3   if(assertionsEnabled && !predicate())
4     throw new AssertionError

  通常的函数参数,() => Boolean是函数类型,调用方式:myAssert(() =5 >3 ),很别扭

1 def myNameAssert(predicate:  => Boolean ) =
2   if(assertionsEnabled && !predicate)
3     throw new AssertionError

    上面是传名参数的方式,调用方式:myNameAssert(5>3),很正常,可读性很好

      这里有些地方需要理解(个人看法,觉得不对的,可以告诉我):

      myNameAssert中的参数是函数,首先传入参数应该是个函数。

  再者,传名参数的函数,后面使用时,只用函数名不加括号,所以传名参数的这种函数

  应该只适用于无参的函数,所以在声明函数时,可以将()省略。调用时,也不用写传入的参数。

  因此,传进来的表达式,应该用做了函数体,最后一行是返回值。

  所以,这个myNameAssert(5>3)中5>3,应该对应函数参数的函数体。

1 def boolAssert(predicate: Boolean ) =
2   if(assertionsEnabled && !predicate)
3     throw new AssertionError

  上面是传值参数,调用方式::myNameAssert(5>3)。

  传名参数同时具有传值参数的可读性,同时,在正真执行函数的时,才会真正计算,带入函数。

传名函数举例:

1 val logEnable = false
2 def log(msg: => String) =
3     if (logEnable) println(msg)
4 val MSG = "programing is running"
5 log(MSG + 1 / 0)
虽然表达式:MSG + 1 / 0中有不合法的,但是因为函数是传名参数,只有真正执行时,才会带入计算。在本例中,println(msg)执行不到,所以不会报错。

 在scala中方法是针对对象的,函数是没有这样的限制的 

  val fun = ceil _

  后面的_,就表示这个ceil确实是函数(已经将方法转换成了函数)

如果参数在=>右侧只出现了一次,可以用_来代替

  valueAtOneQuarter( x => 3*x)

  可以换成valueAtOneQuarter(3*_)

函数组合:

让我们创建两个函数:

scala> def f(s: String) = "f(" + s + ")"
f: (String)java.lang.String

scala> def g(s: String) = "g(" + s + ")"
g: (String)java.lang.String

compose

compose 组合其他函数形成一个新的函数 f(g(x))

scala> val fComposeG = f _ compose g _
fComposeG: (String) => java.lang.String = <function>

scala> fComposeG("yay")
res0: java.lang.String = f(g(yay))

andThen

andThen 和 compose很像,但是调用顺序是先调用第一个函数,然后调用第二个,即g(f(x))

scala> val fAndThenG = f _ andThen g _
fAndThenG: (String) => java.lang.String = <function>

scala> fAndThenG("yay")
res1: java.lang.String = g(f(yay))

注意compose先调用第二个

     andThen先调用第一个

偏函数:

对于函数而言,可以传入任何符合输入参数类型的值。

但是偏函数的输入参数,除了类型符合之外,还必须是定义了的特殊值。

isDefinedAt 是PartialFunction的一个方法,用来确定PartialFunction是否能接受一个给定的参数。

偏函数PartialFunction 和部分应用函数是无关的。

scala> val one: PartialFunction[Int, String] = { case 1 => "one" }
one: PartialFunction[Int,String] = <function1>

scala> one.isDefinedAt(1)
res0: Boolean = true

scala> one.isDefinedAt(2)
res1: Boolean = false

您可以调用一个偏函数。

scala> one(1)
res2: String = one

PartialFunctions可以使用orElse组成新的函数,得到的PartialFunction反映了是否对给定参数进行了定义。

scala> val two: PartialFunction[Int, String] = { case 2 => "two" }
two: PartialFunction[Int,String] = <function1>

scala> val three: PartialFunction[Int, String] = { case 3 => "three" }
three: PartialFunction[Int,String] = <function1>

scala> val wildcard: PartialFunction[Int, String] = { case _ => "something else" }
wildcard: PartialFunction[Int,String] = <function1>

scala> val partial = one orElse two orElse three orElse wildcard
partial: PartialFunction[Int,String] = <function1>

scala> partial(5)
res24: String = something else

scala> partial(3)
res25: String = three

scala> partial(2)
res26: String = two

scala> partial(1)
res27: String = one

scala> partial(0)
res28: String = something else

模式匹配中的case:

case是PartialFunction函数的子类。

多个case语句的集合,实质上是共同组合在一起的多个PartialFunction。如下:

val partial = one orElse two orElse three orElse wildcard

我们见过这样的代码:
scala> case class PhoneExt(name: String, ext: Int)
defined class PhoneExt

scala> val extensions = List(PhoneExt("steve", 100), PhoneExt("robey", 200))
extensions: List[PhoneExt] = List(PhoneExt(steve,100), PhoneExt(robey,200))

scala> extensions.filter { case PhoneExt(name, extension) => extension < 200 }
res0: List[PhoneExt] = List(PhoneExt(steve,100))

为什么filter中可以使用case语句呢?(filter中应该接受一个谓词函数)

这是因为case是PartialFunction的子类,而PartialFunction是Function的子类。所以,case语句也可以被看成一个函数,只要他的返回值是一个Boolean类型,就可以了。