在函数式编程语言中,函数是“头等公民”,可以像任何其他数据类型一样被传递和操作。因为Scala混合了面向对象和函数式的特性,所以对Scala来说,函数是“头等公民”。
作为值的函数
|
importscala.math._
valfun=ceil_ // _将ceil方法转成了函数
|
在Scala中,无法直接操纵方法,只能直接操纵函数,所以需要使用_。
fun的类型是(Double)=>Double,意为接受Double参数并返回Double的函数。能够对fun做的有:调用,传递。
|
valnum=3.14
fun(num) // 返回4.0,调用fun
Array(3.14,1.42,2.0).map(fun) //返回Array(4.0, 2.0, 2.0),将fun作为变量传递
|
匿名函数
函数不一定需要名称:
|
(x:Double)=>3*x // 该匿名函数将传给它的参数乘3
|
可以将匿名函数赋值给变量,也可以当参数传递。
带函数参数的函数
如何实现一个接受另一个函数为参数的函数:
|
defvalueAtOneQuarter(f:(Double)=>Double)=f(0.25)
|
该函数的类型是: ((Double) => Double) => Double。
还有可以返回一个函数的函数:
|
defmulBy(factor:Double)=(x:Double)=>factor*x
// mulBy可以产出任何两个数相乘的函数
valquintuple=mulBy(5) // (x: Double) => 5 * x
quintuple(20) // 5 * 20
|
这样接受函数参数,或者是返回函数的函数,被称为高阶函数(higher-order function)。
参数(类型)推断
前面有定义高阶函数 def valueAtOneQuarter(f: (Double) => Double) = f(0.25),因为已知参数的类型,所以Scala会尽可能推断出类型,在传入参数时,可以省掉一些内容。
|
valueAtOneQuarter((x:Double)=>3*x) // 完整写法
valueAtOneQuarter((x)=>3*x) // 已知参数类型,可以省掉Double
valueAtOneQuarter(x=>3*x) // 只有一个参数时,可以省去()
valueAtOneQuarter(3*_) // 参数只在右侧出现一次,可以用_替换
|
闭包
闭包(closure)这个概念,虽然差不多能懂,但解释不清楚的感觉,参考一下闭包的维基百科。
SAM转换
在Scala中,要某个函数做某件事时,会传一个函数参数给它。而在Java中,并不支持传送参数。通常Java的实现方式是将动作放在一个实现某接口的类中,然后将该类的一个实例传递给另一个方法。很多时候,这些接口只有单个抽象方法(single abstract method),在Java中被称为SAM类型。
举例,点击一个按钮时,增加一个计数器:
|
varcounter=0
valbutton=newJButton("Increment")
button.addActionListener(newActionListener{
overridedefactionPerformed(event:ActionEvent){
count+=1
}
})
|
这是非常常见的,给按钮添加监听器的代码。其实只要给addActionListener传一个函数参数,也就能够实现一样的功能了。
|
button.addActionListener((event:ActionEvent)=>counter+=1)
|
为了使这个语法真的生效,需要提供一个隐式转换。隐式转换将在21章详述。下面是简单的示例:
|
implicitdefmakeAction(action:(ActionEvent)=>Unit)=
newActionListener{
overridedefactionPerformed(event:ActionEvent){action(event)}
}
|
将这个函数和界面代码放在一起,就可以在所有预期ActionListener对象的地方,传入(ActionEvent)=>Unit函数参数。
从上面的代码可以看出,隐式转换就是将一种类型自动转换成另外一种类型,是个函数。因为在Scala中,函数是头等公民,所以隐式转换的作用也大大放大了。
柯里化(Currying)
柯里化的概念也请参考维基百科。
柯里化指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。
|
defmulOneAtATime(x:Int)=(y:Int)=>x*y
// 计算两个数的乘积
mulOneAtATime(6)(7)
// 多参数的写法
defmul(x:Int,y:Int)=x*y
|
mulOneAtATime(6)返回的是函数(y: Int)=>6*y,再将这个函数应用到7,最终得到结果。
柯里化函数可以在Scala中简写:
|
defmulOneAtATime(x:Int)(y:Int)=x*y
|
多参数是个虚饰,不是编程语言的根本性的特质。
可以利用柯里化把某个函数参数单独拎出来,提供更多用于类型推断的信息。
|
vala=Array("Hello","World")
valb=Array("hello","world")
a.corresponds(b)(_.equalsIgnoreCase(_))
|
corresponds的类型声明如下:
|
defcorresponds[B](that:GenSeq[B])(p:(T,B)⇒Boolean):Boolean
|
方法有两个参数,that序列和f函数,其中f函数有两个参数,第二个参数类型是与that序列一致的。因为使用了柯里化,我们可以省去第二个参数中B的类型,因为从that序列中推断出B的类型。于是,_equalsIgnoreCase(_)这个简写就符合参数的要求了。
控制抽象
Scala中,可以将一系列语句归组成不带参数也没有返回值的函数。
|
defrunInThread(block:()=>Unit){
newThread{
overridedefrun(){block()}
}.start()
}
// 调用
runInThread{()=>println("Hi");Thread.sleep(10000);println("Bye")}
|
可以去掉调用中的()=>,在参数声明和调用该函数参数的地方略去(),保留=>。
|
defrunInThread(block:=>Unit){
newThread{
overridedefrun(){block}
}.start()
}
// 调用
runInThread{println("Hi");Thread.sleep(10000);println("Bye")}
|
Scala程序员可以构建控制抽象:看上去像是编程语言关键字的函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
defuntil(condition:=>Boolean)(block:=>Unit){
if(!condition){
block
until(condition)(block)
}
}
// 使用
varx=10
until(x==0){
x-=1
println(x)
}
|
这样的函数参数专业术语叫做换名调用参数(常规的参数叫换值调用参数)。函数在调用时,换名调用参数的表达式不会被求值,表达式会被当做参数传递下去。
return表达式
一般不需要使用return来返回函数值。但是return可以用来从一个匿名函数中返回值给包含这个匿名函数的带名函数,对于控制抽象来说是很有用的。如果要在带名参数中使用return,需要在定义中给出返回类型。
|
defindexOf(str:String,ch:Char):Int={
vari=0
util(i==str.length){
if(str(i)==str.length)returni
i+=1
}
return-1
}
|
在这里,util这个抽象控制中的return语句,会使外部的带名函数indexOf终止并且返回i的值。
这里控制流程的实现依赖于在匿名函数中return表达式抛出的特殊异常。如果这个异常在被送往带名函数前被捕获,那么就无法为带名函数返回值了,这一点需要注意
相关推荐
尽管函数式编程在近十多年用得越来越多,但市面上介绍其高阶特性的书却并不多。这本书在这方面是个重要的补充,它不仅仅面向 Scala 程序员,同样面向用任何编程语言开发的程序员,只要你充满好奇心。 ——挖财网...
在学习Scala的过程中,这些习题将覆盖诸如基本语法、类与对象、模式匹配、高阶函数、类型系统、集合框架、并发编程、 Actors模型、Scala与其他Java平台的互操作性等核心主题。通过解决这些习题,学习者可以了解如何...
通过这种方式,开发者可以开始探索Scala的深度和广度,例如利用高阶函数、模式匹配、Actor模型等特性进行大数据处理。 总结,Scala作为大数据技术的重要组成部分,因其与Spark的紧密关系和独特的编程风格,逐渐受到...
2. 函数式编程:学习如何使用高阶函数、闭包、柯里化等概念,以及如何编写纯函数。 3. 泛型:了解如何使用泛型来创建类型安全的代码,提高代码的重用性。 4. 隐式转换和参数:理解隐式转换和隐式参数的用途,以及...
Scala程序设计(第2版)很可能详尽地介绍了Scala的基本语法、类与对象、模式匹配、高阶函数、类型系统、并发编程等核心概念,以及可能包含了一些新的语言特性或最佳实践。 **JAVA虚拟机多核编程实战** 这本书则关注...
Scala的语法优雅且富有表达力,它允许开发者使用函数式编程的特性,如高阶函数、柯里化、模式匹配和不可变数据结构,同时保留了面向对象的类、接口和继承。这使得Scala成为处理大数据、并发和分布式计算的理想选择,...
函数式编程的关键特性,如高阶函数、柯里化(currying)、闭包和尾递归,在Scala中都有很好的支持。例如,`map`、`filter`和`reduce`等函数是处理集合的常用方法,它们让代码更加简洁和易于理解。 Scala还引入了...
2. **函数式编程**:Scala 具有丰富的函数式编程特性,如高阶函数、柯里化(currying)、闭包和尾递归。这对于处理并发和复杂数据结构非常有用,因为它鼓励编写无副作用的代码。 3. ** Actors 模型**:Scala 内置对...
- **面向对象+函数式编程**:Scala融合了面向对象编程和函数式编程的特性,支持类、对象和继承的同时也支持高阶函数、不可变性等函数式编程的核心概念。 - **分布式运行**:Scala设计之初就考虑到了并行和分布式...
- **示例:寻找函数的不动点**:通过具体例子展示了如何使用高阶函数解决实际问题。 - **总结**:回顾了前面章节中所学到的一等公民函数的相关知识点。 - **语言元素总结**:列出了到目前为止已经介绍过的Scala语言...
- 函数式编程支持:提供高阶函数、柯里化、模式匹配等特性,便于编写简洁而富有表达力的代码。 - 面向对象编程:支持类、接口、继承、多态等面向对象概念。 - REPL(Read-Eval-Print Loop):交互式编程环境,便于...
函数式编程的一个核心概念——高阶函数,也会在此处被介绍,它是处理集合数据的强大工具。 接下来,你将探索Scala的面向对象特性,如类、对象、继承、封装和多态。Scala中的类和对象设计十分灵活,允许定义特质...
安装Scala 2.11.7后,开发者可以开始学习和使用Scala的语法,例如模式匹配、高阶函数、类型系统(包括类型推断和泛型)以及Actor模型等特性,这些都让Scala在处理并发和大数据处理方面表现出色。此外,Scala是Spark...
在实际开发中,Scala的特性如类型推断、模式匹配、高阶函数等,都能在IDE的辅助下得到更好的理解和应用。例如,类型推断使得开发者无需显式声明变量类型,而模式匹配则简化了数据解析和处理的过程。高阶函数则允许将...
它不仅支持面向对象编程的所有特性——如类、继承等,同时还支持函数式编程的核心概念——如高阶函数、模式匹配等。这种独特的混合范式使得Scala能够满足不同应用场景的需求,无论是构建大规模的企业应用还是进行...
本书由Scala之父作序推荐,深入探讨了Scala里几个较为复杂的领域,包括类型系统的高阶内容、隐式转换、特质的组合技巧、集合、Actor、函数式编程的范畴论等,而且不是干巴巴地讲述语言和库的概念。本书示例丰富,是...
- **函数式编程特性**:Scala同样支持函数式编程的关键概念,如高阶函数和嵌套函数定义。此外,Scala还支持模式匹配,这在处理复杂的数据结构时尤其有用。 #### 二、Scala与Java的互操作性 Scala的一个显著优势...
3. **函数式编程**:Scala深度支持函数式编程,如高阶函数、柯里化、闭包、不可变数据结构和模式匹配。函数式编程的特点在于避免状态改变和副作用,以提高代码的可读性和可维护性。 4. **类型系统**:Scala有一个...