函数是一等公民
可以使用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类型,就可以了。
浙公网安备 33010602011771号