上节中processFile方法的建立演示了函数式编程风格的一个重要设计原则:程序应该被解构成若干小的函数,每个完成一个定义良好的任务。单个函数经常很小。这种风格的好处是它给了程序员许多可以灵活组装成更复杂事物的建造模块。每个小块应该充分简化到足以单独理解。
这种方式的一个问题是所有这些帮助函数的名称会污染程序的命名空间。在解释器里这不太成问题,但是一旦函数被打包成可复用的类和对象,就最好对类的客户隐藏帮助函数。它们经常不能独立表达什么意思,并且如果之后用其它方式重写类的话,也常会想保持能删掉帮助方法的足够的灵活度。
Java里,达成这个目的的主要工具是private方法。这种私有方法的方式在Scala里同样有效,如代码8.1里描述的,但是Scala提供了另一种方式:你可以把函数定义在另一个函数中。就好象本地变量那样,这种本地函数仅在包含它的代码块中可见。以下是一个例子:
在这个例子中,我们通过把私有方法,processLine,转换为本地方法,processFile,重构了展示在代码8.1中原本的LongLines版本。为了做到这点我们去掉了private修饰符,它仅能应用于方法(并且仅被方法需要),然后把processLine的定义放在processFile的定义里。作为本地函数,processLine的范围局限于processFile之内,外部无法访问。
- def processFile(filename: String, width: Int) {
- def processLine(filename:String, width:Int, line:String) {
- if (line.length > width) print(filename+": "+line)
- }
- val source = Source.fromFile(filename)
- for (line < - source.getLines) {
- processLine(filename, width, line)
- }
- }
既然processLine被定义在processFile里,另一个改善变为可能了。请注意filename和width是怎样不改变地传入到帮助函数中。这不是必须的,因为本地函数可以访问包含它们的函数的参数。你可以直接使用外部processLine函数的参数,如代码8.2所示:
代码 8.2 带本地processLine方法的LongLines
- import scala.io.Source
- object LongLines {
- def processFile(filename: String, width: Int) {
- def processLine(line: String) {
- if (line.length > width)
- print(filename +": "+ line)
- }
- val source = Source.fromFile(filename)
- for (line < - source.getLines)
- processLine(line)
- }
- }
更简单了,不是吗?这种对外层函数的参数的使用是Scala提供的通用嵌套的很平常也很有用的例子。7.7节描述的嵌套和作用域应用于所有的Scala架构,包括函数。这是一个简单的原则,不过非常强大,尤其在拥有函数作为第一类值的语言中。
【相关阅读】
- Scala中定义函数的方法:method
- 继续领悟函数式:Scala指令式风格代码的重构
- 学习Scala的变量范围
- Scala:match表达式、break和continue
- 学习Scala:使用try-catch表达式处理异常