scala成长之路:函数入门

  众所周知,scala作为一门极客型的函数式编程语言,支持的特性包括:

  ·函数拥有“一等公民”身份;

  ·支持匿名函数(函数字面量)

  ·支持高阶函数

  ·支持闭包

  ·部分应用函数

  ·柯里化

  首先需要指出,在scala中有方法和函数对象两种形态,方法即是通过def关键字定义的函数,而函数对象则是通过将方法转换而来,或lambda赋值而来。

  1. 从“一等公民”说起

  很多稍微了解过函数式编程的人可能都听说过“一等公民”这种说法,但却很少有人能明明白白地说出究竟什么是“一等公民”。这里我做个类比你马上就明白了:现实中,什么样的人能被当做一等公民?首先,他必须是个独立的个体——依赖父母或朋友才能生存的人肯定不能被当做公民,更不用说一等了;其次,这个人必须拥有足够的自由——既能上九天揽月,又可下五洋捉鳖,方才能是一等公民。对应到我们的函数式编程,我们可以总结出几个点:

  (1) 函数的定义和调用不依赖其他结构,例如C、python、js、scala,而反面典型就是java,因为java的任何函数(方法)都必须定义在类、接口、枚举(其实也是类)中,而且任何的方法调用都要通过对象、类的静态方法或接口(jdk 1.8),方法不可能直接调用,必须依附于其他结构而存在。所以这种情况下函数肯定不是“一等公民”。

  (2)函数可以作为函数的参数、返回值,并可以对函数进行变量赋值,而且函数的定义位置极度自由,任何代码块里又能定义函数。

  现在我们再来看scala,它完美地契合上边所有的需求(但是注意,除了脚本形式的scala之外,其他的scala程序也只能包含在class或object中),scala中函数支持在函数内部定义,而且使用lambda表达式定义的函数可以赋值给任何变量、常量,所有函数均可作为返回值、参数。

  2. lambda表达式的学问

  很多scala初学者都倒在了scala的lambda上,因为scala lambda的灵活多样,导致很多时候你可能都看不懂。下面我们从最基本的讲起:

  最基本的:

  valfun=(a:Int)={a100a0}

  当r定义的参数为函数时:

  deffun1(f:String=Unit)=f(wangyalou)

  我们可以方便地使用lambda传入需要的函数:

  fun1((s:String)={println(s)})

  注意了,一般人都不这么写,因为作为参数的lambda可~以~简~写~~~~~准备好我要开始啰!首先,省略掉可以推断出来的类型参数:

  fun1((s)={println(s)})

  当只有一个参数时,=前的()可省:

  fun1(s={println(s)})

  还可再简化,scala中可以用_代替只出现一次的参数:

  fun1(println(_))或fun1(println_)

  最后,我们甚至连下划线都可以不要了:

  fun1(println)

  注意,最后的情况我们是利用了编译器支持lambda的“eta转换”,即在表达式只有一个参数,且整个执行部分就是一个函数调用时,可以直接写函数名

  插一句:eta扩展(eta-expression)是另一个东西,指的是将一个普通方法转换为函数对象的过程:

  valb=too(_,_,_)

  valb=too_//这也可以?是的

  valb:(Int,Int,Int)=Int=foo

  其中too为一个参数为3个Int的方法

  但是,too(_,_,1)一定不是eta扩展!

  下划线的用法博大精深,这里再给出一些例子:

  

  例1:lambda作为参数,_可代替只用一次的参数,且省掉“=”

  valnums=Array(1,2,3,4)

  nums.filter(_2)

  运行结果:

  res31:Array[Int]=Array(3,4)

  例2:lambda作为参数,_可代替只用一次的参数,且省掉“=”

  scala def foo(f:(Int,Int)=Int)(a:Int,b:Int) = f(a,b)

  foo: (f: (Int, Int) = Int)(a: Int, b: Int)Int

  scala foo(_+_)(3,4)

  res33: Int = 7

  例3:lambda作为函数定义,_可代替只用一次的参数,且省掉“=”,但这时要加上类型,因为这里无法推断出“_”的类型

  val b = (_:Int) + (_:Int)

  b(1,2)

  运行结果:

  res32: Int = 3

  3. 部分应用函数(偏函数)

  一个例子足以说清楚:

  scaladeffoo(a:Int,b:Int,c:Boolean)=if(c)a+belsea-b

  foo:(a:Int,b:Int,c:Boolean)Int

  scalavalfoom=foo(_:Int,_:Int,false)foom:(Int,Int)=Int=function2

  类似于python中的偏函数,这里将某个参数确定,其他参数用_代替并指明其类型,注意一定要指明类型啊!!!不然就成了eta扩展失败的案例了!!

  

猜你喜欢

转载自blog.csdn.net/qianfeng_dashuju/article/details/82426002
今日推荐