理解Scala的函数式风格:从var到val的转变

转载 2015年11月21日 10:53:55

本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第二章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

AD:【活动】Web和APP兼容性实战 Win10训练营免费报名

Scala允许你用指令式风格编程,但是鼓励你采用一种更函数式的风格。如果你是从指令式的背景转到Scala来的——例如,如果你是Java程序员——那么学习Scala是你有可能面对的主要挑战就是理解怎样用函数式的风格编程。我们明白这种转变会很困难,在本书中我们将竭尽所能把你向这方面引导。不过这也需要你这方面的一些工作,我们鼓励你付出努力。如果你来自于指令式的背景,我们相信学习用函数式风格编程将不仅让你变成更好的Scala程序员,而且还能拓展你的视野并使你变成通常意义上好的程序员。

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

通向更函数式风格路上的第一步是识别这两种风格在代码上的差异。其中的一点蛛丝马迹就是,如果代码包含了任何var变量,那它大概就是指令式的风格。如果代码根本就没有var——就是说仅仅包含val——那它大概是函数式的风格。因此向函数式风格推进的一个方式,就是尝试不用任何var编程。

如果你来自于指令式的背景,如Java,C++,或者C#,你或许认为var是很正统的变量而val是一种特殊类型的变量。相反,如果你来自于函数式背景,如Haskell,OCamel,或Erlang,你或许认为val是一种正统的变量而var有亵渎神灵的血统。然而在Scala看来,val和var只不过是你工具箱里两种不同的工具。它们都很有用,没有一个天生是魔鬼。Scala鼓励你学习val,但也不会责怪你对给定的工作选择最有效的工具。尽管或许你同意这种平衡的哲学,你或许仍然发现第一次理解如何从你的代码中去掉var是很挑战的事情。
考虑下面这个改自于第2章的while循环例子,它使用了var并因此属于指令式风格:

  1. def printArgs(args: Array[String]): Unit = {  
  2.  var i = 0 
  3.  while (i < args.length) {  
  4.   println(args(i))  
  5.   i += 1  
  6.  }  
  7. }  

你可以通过去掉var的办法把这个代码变得更函数式风格,例如,像这样:

  1. def printArgs(args: Array[String]): Unit = {  
  2.  for (arg <- args)  
  3.   println(arg)  
  4. }  

或这样:

  1. def printArgs(args: Array[String]): Unit = {  
  2.  args.foreach(println)  
  3. }  

这个例子演示了减少使用var的一个好处。重构后(更函数式)的代码比原来(更指令式)的代码更简洁,明白,也更少机会犯错。Scala鼓励函数式风格的原因,实际上也就是因为函数式风格可以帮助你写出更易读懂,更不容易犯错的代码。

当然,你可以走得更远。重构后的printArgs方法并不是纯函数式的,因为它有副作用——本例中,其副作用是打印到标准输出流。函数有副作用的马脚就是结果类型为Unit。如果某个函数不返回任何有用的值,就是说其结果类型为Unit,那么那个函数唯一能让世界有点儿变化的办法就是通过某种副作用。更函数式的方式应该是定义对需打印的arg进行格式化的方法,但是仅返回格式化之后的字串,如代码3.9所示:

  1. def formatArgs(args: Array[String]) = args.mkString("\n") 

代码 3.9 没有副作用或var的函数

现在才是真正函数式风格的了:满眼看不到副作用或者var。能在任何可枚举的集合类型(包括数组,列表,集和映射)上调用的mkString方法,返回由每个数组元素调用toString产生结果组成的字串,以传入字串间隔。因此如果args包含了三个元素,"zero","one"和"two",formatArgs将返回"zero\none\ntwo"。当然,这个函数并不像printArgs方法那样实际打印输出,但可以简单地把它的结果传递给println来实现:

  1. println(formatArgs(args)) 

每个有用的程序都可能有某种形式的副作用,因为否则就不可能对外部世界提供什么值。偏好于无副作用的方法可以鼓励你设计副作用代码最少化了的程序。这种方式的好处之一是可以有助于使你的程序更容易测试。举例来说,要测试本节之前给出三段printArgs方法的任一个,你将需要重定义println,捕获传递给它的输出,并确信这是你希望的。相反,你可以通过检查结果来测试formatArgs:

  1. val res = formatArgs(Array("zero", "one", "two"))  
  2. assert(res == "zero\none\ntwo")  

Scala的assert方法检查传入的Boolean并且如果是假,抛出AssertionError。如果传入的Boolean是真,assert只是静静地返回。你将在第十四章学习更多关于断言和测试的东西。

虽如此说,不过请牢记在心:不管是var还是副作用都不是天生邪恶的。Scala不是强迫你用函数式风格编任何东西的纯函数式语言。它是一种指令式/函数式混合的语言。你或许发现在某些情况下指令式风格更符合你手中的问题,在这时候你不应该对使用它犹豫不决。

Scala程序员的平衡感

崇尚val,不可变对象和没有副作用的方法。

首先想到它们。只有在特定需要和判断之后才选择var,可变对象和有副作用的方法。

相关文章推荐

Scala之集合上常见的函数式风格的操作汇总

目录过滤相关的操作 filter 解释 示例 partition 解释 示例 find 解释 示例 takeWhile 解释 示例 映射相关的操作 map 解释 示例 flatMap 解释 示例 示例...

【Scala编程】函数式风格编写排序算法

有关Scala编程实例在刚开始学习一门编程语言的时候,总是想去写一些比较大的程序和项目,但是由于基础不扎实,往往欲速则不达。所以,只能一步一步来,通过一些经典的小例子来实践和锻炼,最终不断加深编程的技...

Scala的函数式风格

Scala的魅力之一就是其函数式编程风格实现。 1 Scala函数之常规函数 eg. def add ( i:Int, j:Int ) :Int = i + j (1)def 是函数定义...

scala akka 修炼之路6(scala函数式柯里化风格应用场景分析)

scala的函数式和面向对象风格,可以让想想随时发生;如果你是画家,使用scala写出来的代码更像一幅充满诗意的风景画。如果你是作家,写出的将是一个扣人心弦的跌宕起伏的大篇。scala给不同类型的程序...

Scala学习笔记03【学习识别Scala函数式风格】

Scala允许用指令式风格编程,但是鼓励采用一种更函数式的风格。学习用函数式风格编程将不仅让你变成更好的Scala程序员,而且还能拓展你的视野并使你变成通常意义上好的程序员。指令式和函数式编程差异: ...

scala中的var,val,immutable,mutable理解小结

最近项目要用到spark平台,无论是看源码还是写程序都需要用scala语言,接触了两天之后,对映射Map这里有点疑问,做了点测试之后记一记自己的想法。        首先,在scala中定义变量有...
  • lhui798
  • lhui798
  • 2016年09月08日 15:58
  • 198

Scala函数式编

  • 2017年06月23日 13:31
  • 64.77MB
  • 下载

简谈scala 中的val 与 var

断断续续学习scala也有一段时间了,初期总对val 与var 的理解不太透彻,今天来做做总结。一般都知道val 表示不可变,var表示可变,比如: val s ="hello" s="wor...

Scala 的 val与var

摘自                                               “Scala 编程       Martin Odersky, Lex Spoon, Bill Ven...

scala val和var变量

scala中一切皆为对象,对象皆有方法 --变量 val 不可变变量,类似java中的final var 可变变量 scala> val result=2+10 result: Int = 12 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:理解Scala的函数式风格:从var到val的转变
举报原因:
原因补充:

(最多只允许输入30个字)