众所周知, 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 的灵活多样, 导致很多时候你可能都看不懂. 下面我们从最基本的讲起:
最基本的:
val fun = (a:Int) => {a <100 && a> 0}
当 r 定义的参数为函数时:
def fun1(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) 是另一个东西, 指的是将一个普通方法转换为函数对象的过程:
- val b= too(_,_,_)
- val b= too _ // 这也可以? 是的
- val b : (Int,Int,Int) =>Int = foo
其中 too 为一个参数为 3 个 Int 的方法
但是, too(_,_,1)一定不是 eta 扩展!
下划线的用法博大精深, 这里再给出一些例子:
例 1:lambda 作为参数,_可代替只用一次的参数, 且省掉 "=>"
- val nums = 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. 部分应用函数(偏函数)
一个例子足以说清楚:
- scala> def foo(a:Int, b:Int, c:Boolean) = if(c) a+b else a-b
- foo: (a: Int, b: Int, c: Boolean)Int
- scala> val foom = foo(_:Int,_:Int,false)
- foom: (Int, Int) => Int = <function2>
类似于 python 中的偏函数, 这里将某个参数确定, 其他参数用 "_" 代替并指明其类型, 注意一定要指明类型啊!!! 不然就成了 eta 扩展失败的案例了!!
来源: https://www.cnblogs.com/wangyalou/p/9588941.html