Scala如何改变了我的编程风格:从命令式到函数式

51CTO编辑推荐:
Scala编程语言专题

【51CTO快译】编者前言:这篇文章最初写于2008年底,作者Bill Venners一方面是美国著名开发网站Artima的总编,另一方面也是一位十分关注Scala语言的Java程序员。在这几个月间的Scala创始人Martin Odersky访谈系列中,与Martin对话的正是Bill Venners。这篇文章虽然已经完成了半年有余,但对于还不很熟悉Scala语言的Java程序员而言,仍然是一篇非常实用的Scala语言简介。以下是译文:

每次我学习一门新的语言,我都会学到某些编程方面的东西。比如说,当我以一个C++程序员的身份学习Java的时候,Java的接口构造教会我来自纯粹的抽象基类的多重继承的价值。尽管在C++里面这种编程风格是有可能的,但在我使用C++的日子里,我却没有考虑用这种方式进行多重继承,而我在C++设计中也不怎么使用抽象基类。然而,一旦我开始进行Java编程,我就开始一直使用这种风格了。学习Java—尤其是它的接口构造—改变了我OO设计的方法。

51CTO编辑推荐:
充分利用面向对象语言的接口特性
|
面向对象的思维过程

我学习
Scala编程的时候也发生了类似的情况。在过去的两年里,我有相当多的时间是用
Scala工作的,Scala是Java平台上的一种新的静态类型语言,它融合了面向对象编程和函数型程序设计的概念。Scala能让我写出几乎跟Ruby和Python
一样简洁的代码。在
Scala
我可以跟在
Java
里面一样方便地调用
Java
库,包括我已有的
Java
库。考虑到
Scala
是静态类型的,我可以享受到诸多静态类型的好处,诸如将文档作为类型,
IDE
代码自动完成,动态代码重构(
deterministic refactoring
)以及执行速度等(
Scala
程序的执行速度跟
Java
的一样快)。但
Scala
还让我以简洁和类型安全的方式获得某些通常是动态语言的好处,例如在已有类上增加新方法的能力,或者将类型传递给没有共同继承关系的方法。

Scala
是怎样改变了我对编程的看法的呢?一句话:我学会了欣赏函数化的风格。函数化的编程风格强调不可变对象、变量可被初始化但不能重新赋值(
Java
中的最终变量)、数据结构转换,以及方法和控制的构造,最终产生一个没有副作用的结果。这个领域的另一端是命令式的风格,以可变对象、变量可被重新赋值(
Java
里的正常变量)、在数据结构中索引、以及带副作用的方法和控制构造为特征。

尽管
Scala
经常被吹捧为函数型编程语言,当它并不仅仅是函数型的。
Scala
同时支持函数式和命令式两种风格。如果你自己选择要这么做的话,你可以以
Java
的编程方式进行
Scala
编程,那种风格主要是命令式的。这样有助于
Scala
的学习曲线变缓,但随着对
Scala
越来越熟悉,你就会发现自己会更喜欢函数式的。我就是这样。为什么?因为我发现函数型风格往往要比命令式风格的代码更简洁,且更不易出错。函数式风格的代码通常层次更高,这使得它编写起来更快,阅读也更为容易。举个例子,看看下面这段确定一个字符串是否包含大写字符的
Java
代码。

boolean nameHasUpperCase = false; // 这是Java
for (int i = 0; i < name.length(); ++i) {

    if (Character.isUpperCase(name.charAt(i))) {

        nameHasUpperCase = true;

        break;

    } 

}

这里的命令式风格是很明显的,因为
nameHasUpperCase
变量被重新赋值会给
loop
循环带来副作用,
loop
是通过字符串中的字母索引进行迭代的。在
Java
你还可以以更为简洁的方式得到相同的结果,像下面这样:

boolean nameHasUpperCase = !name.toLowerCase().equals(name);

这一行
Java
代码展现出一种更为函数化的风格,因为它转换不可变数据:
name
这个字符串被转换为另外一个全部字母都是小写的字符串,然后值被转换为布尔结果。此外,
nameHasUpperCase
这个变量被初始化了,但仅限于这一小块代码里,而没有被重新赋值。如果该变量为最终值的话,它的函数化就会更为清晰。


Scala
里面,你可以写出跟以上两个例子类似的代码,不过更为理想的编写方式是像下面这样的:

val nameHasUpperCase = name.exists(_.isUpperCase)

nameHasUpperCase
变量被定义为
val
,即可被初始化但不能被重新赋值的变量(类似于
Java
里面的最终变量)。甚至于尽管本例中并无显式的类型标注,
Scala
的类型推断机制也会给
nameHasUpperCase
赋予
Boolean
类型。
exists
方法在对象集合中迭代,并依次将每个元素传递给函数对象。在这里,
name
字符串被视为字符集合,因此
exists
会把字符串的每一个字符都传递给该函数。
_.isUpperCase
的语法是
Scala
里的一种函数显式声明(
function literal
),是一种编写少量代码就可以到处传递和调用的速写方式。下划线代表该函数的唯一参数。因此你可以把下划线视为每次该函数被调用时待填的空白。如果
exists
方法发现该函数因被传递的字符中的其中一个而返回
true—
比如说,其中一个字符是大写的

而返回
true
。否则就返回
false

尽管最后的这个单行代码对于某些不熟悉Scala的人来说像是天书,只要你了解了
Scala,你就能一眼看出代码的目的。相反,其他的两个版本却要费上一点功夫去研究一下。另外需要注意的一点不同是命令式例子中潜在的偏移错误,因为你必须显式地指出迭代的上标。在函数化的版本里这种错误不会产生,在这种方式下,函数化版本相对而言不易出错。

最后,我想指出的是我转向
Scala
的时候并没有“彻底函数化”。尽管我已经发现通常大部分情况下函数化风格的代码来得更为简洁、明晰,更不易出错,我还发现有时候命令式风格也可带来更为清晰和简洁的代码。在那种情况下,我就会使用命令式的。
Scala
允许我方便地应用函数式和命令式的风格,结合使用此二者,我就能找到写出清晰代码的最佳方式。

函数式编程和命令式编程简介

什么是函数式编程?(参考资料:《征服RIA:基于JavaScript的Web客户端开发》第8章JavaScript函数对象
在数学领域,函数是一种关系,这种关系使一个集合里的每一个元素对应到另一个集合里的唯一元素。函数是将唯一的输出值赋予每一输入的"法则"。这一"法则"可以用函数表达式、数学关系,或者一个将输入值与输出值对应列出的简单表格来表示。函数最重要的性质是其决定性,即同一输入总是对应同一输出(注意,反之未必成立)。从这种视角,可以将函数看做"机器"或者"黑盒",它将有效的输入值变换为唯一的输出值。通常将输入值称做函数的参数,将输出值称做函数的值。
《Why Functional Programming Matters》的作者John Hughes 说明了模块化是成功编程的关键,而函数编程可以极大地改进模块化。在函数编程中,编程人员有一个天然框架用来开发更精练的、更小的、更简单的和更一般化的模块,然后将它们组合在一起。函数式编程的基本特点是:
丰富的数据类型;
函数是运算元;
在函数内保存数据;
函数内的运算对函数外无副作用。
函数式编程只描述在程序输入上执行的操作,不必使用临时变量保存中间结果。重点是捕捉"是什么以及为什么",而不是"如何做"。与将重点放在执行连续命令上的过程性编程相比,函数式编程的重点是函数的定义而不是状态机(State Machine)的实现。是一种强调表达式的计算而非命令的执行的一种编程风格。表达式是用函数结合基本值构成的,它类似于用参数调用函数(函数式的优美的说明可见《Functional Programming For The Rest of Us》)。
什么是命令式编程?(参考资料:维基百科)
命令式编程,是种描述电脑所需作出的行为的编程典范。几乎所有电脑的硬体工作都是命令式的;几乎所有电脑的硬体都是设计来执行机器码,使用命令式的风格来写的。较高阶的命令式编程语言使用变数和更复杂的语句,但仍依从相同的典范。食谱和行动清单,虽非电脑程式,但与命令式编程有相似的风格:每步都是指令,有形的世界控制情况。因为命令式编程的基础观念,不但概念上比较熟悉,而且较容易具体表现于硬体,所以大部分的编程语言都是命令式的。
原文:How Scala Changed My Programming Style
作者:Bill Venners

【相关阅读】
Scala的类型系统 比Java更灵活
Java程序员,你为什么要关注Scala

Scala创始人:创造比Java更好的语言

Java以外的选择 Scala编程语言简介

Java之外,选择Scala还是Groovy?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容