函数式编程是近来很热的概念,scala是一种典型的函数式编程语言,并且它与Java有很好的集成性。很多有名的开源产品都采用了scala,比如:kafka、tranquility、samza。因为工作需要调研以上产品,所以是时候学习scala了。
自己本身主语言是Java,并且没有了解过函数式语言。Java是一种命令式的编程语言,它与函数式语言在思维方式上有巨大的区别。scala与Java最大区别是函数特性。这篇文章不介绍scala语法特性细节,也不介绍函数式编程的概念,本文是自己在学习scala的时候对函数式编程这一特性的感受与理解。主要以代码的形式展现,相信代码是工程师之间交流最好的方式。
函数式与命令式
我们采用举例与对比的方式,例子中实现++的功能。首先一个命令式(Java)的例子:
int counter;
void inc(){
counter++;
}
再看函数式的写法(scala):
def inc(counter: Int){
counter + 1;
}
例子看起来很简单,但是它确实的反映了函数式编程几个重要的特性:
1. 强调不可变性
2. 强调不依赖外部数据
3. 面向表达式而不是语句
其中不可变性指的是,当需要改变变量的值的时候,不是直接改变而是采用==copy and change==的方式。不依赖外部数据指的是,函数所用的数据都通过参数传递进去,而不是使用公共数据。表达式与语句相对应,表达式都会返回值,所以面向表达式指的是函数都会有返回值。
函数式编程最初的设计用途是科学计算,它的特性都是为了==传入参数,函数加工,返回结果==而设计。同时这种设计也带来了一些优势。
- 由于函数式编程中强调不可变性与不使用外部数据(公共数据),这样天然避免了并发时的同步问题
- 函数组合与嵌套的特性,极大方便了对某些问题的描述
- scala提供大量集合高阶函数,使得集合操作充满简便性与创造性
函数即对象
让我们一步一步解析scala中函数即对象的意思
apply方法
当类或对象有一个主要用途的时候,apply方法提供了一个很好的语法糖。
scala> class Foo (){ }
defined class Foo
scala> object foo{
| def apply() = { println("I am Foo!") }
| }
defined object foo
scala> foo()
I am Foo!
中我们通过“foo()”的方式调用了对象“foo”的apply方法。
进一步我们给apply方法添加参数。
scala> class Foo (){ }
defined class Foo
scala> object foo{
| def apply(s: String) = { println(s) }
| }
defined object foo
scala> foo("I am Foo!")
I am Foo!
Function
scala中预定义了22个Function,从Function1到Function22,每个Function中定义了函数apply,apply函数需要传入的参数个数分别从1到22。scala中的对象通过继承Function可以很方便的使用applay语法糖。
scala> object addOne extends Function1[Int, Int]{
| override def apply(m: Int): Int = m + 1
| }
defined object addOne
scala> addOne(2)
res2: Int = 3
函数即对象
请看下面的示例:
scala> val addOne = { (i: Int) => i+1 }
addOne: Int => Int = <function1>
scala> addOne(1)
res1: Int = 2
其中
val addOne = (i: Int) => i+1
是一个函数,它与上一小节中的形式是等价的,scala内部会把它转化成对象的形式并继承Function1。这样就可以使用了apply语法糖。scala正式借由Function与apply实现了函数即对象的语法特性。
面向表达式
scala是一个高度面向表达式的语言,表达式又是什么的呢?
简单表达式
var v1 = 1
val v2 = 1 + 1
使用函数的表达式
val v3 = addOne(1)
带有流程控制的表达式
var type: String = "T1"
val v4:Int = if (type == "T1") {
1
} else if (type == "T2") {
2
else {
3
}
函数表达式形式
scala中函数表达式的形式可以是多种形态的
定义一个类”Counter”,并实现”add”方法
scala> class Counter(nu: Int) {
| var num = nu;
| def add(c: Counter): Counter = {
| return new Counter(this.num + c.num);
| }
| }
defined class Counter
scala> val c1 = new Counter(1);
c1: Counter = Counter@29f0802c
scala> val c2 = new Counter(2);
c2: Counter = Counter@516ebdf8
scala> val c3 = c1.add(c2)
c3: Counter = Counter@36bc415e
scala> c3.num
res4: Int = 3
scala中可以使用空格替换表示从属关系的”.”,则”add”可以写成如下形式
scala> val c3 = c1 add c2
c3: Counter = Counter@36bc415e
scala> c3.num
res4: Int = 3
scala中允许方法以及变量名中包含特殊字符,将上例中的方法名改成”++”
scala> val c3 = c1 ++ c2
c3: Counter = Counter@36bc417e
scala> c3.num
res5: Int = 3
通过这两个特性,完成了表达式写法的渐变,并且一步一步接近通常意义的表达式形式
c1.add(c2)
c1 add c2
c1 ++ c2
操作符即函数
上一小节中我们看到可以使用特殊字符如”++”来命名方法与变量,为什么scala中会有这样的设定呢。其实在scala中没有传统意义的操作符,所有的操作符都是函数。
scala> val v = 1 + 1
v: Int = 2
上边表达式中的 “+” 与Java中的不一样,它其实是一个函数。scala是一个彻底的面向对象的语言,没有像Java中的基础数据类型,数字”1″会被装箱成”Int”对象,而”+”则是”Int”的方法,部分”Int”的符号方法如下:
def unary_~ : scala.Int
def unary_+ : scala.Int
def unary_- : scala.Int
scala规定单个符号作为方法名时,需要添加前对”unary_”,但在使用的时候不写前缀。
参考
版权声明:文章为作者辛勤劳动的成果,转载请注明作者与出处。
发表回复