通常我们作为java开发程序员转scala的时候,经常还是习惯性的用var这种命令式的方式来编码。其实scala里有更合适的函数编程
下面的以一个打印文件内容的例子为例,看下两种方案的区别。
这里主要看下widthOfLength的用法
假如是java方式的写法
def widthOfLength(s:String) = {
var maxWidth = 0
for (line <- lines)
maxWidth = maxWidth.max(widthOfLength(line))
}
如果换成函数式,去除var的引用。
def widthOfLength(s: String) = s.length.toString.length
import scala.io.Source
def widthOfLength(s: String) = s.length.toString.length
if (args.length > 0) {
val lines = Source.fromFile(args(0)).getLines().toList
val longestLine = lines.reduceLeft(
(a, b) => if (a.length > b.length) a else b
)
val maxWidth = widthOfLength(longestLine)
}
for (line <- lines) {
val numSpaces = maxWidth - widthOfLength(line)
val padding = " " * numSpaces
println(padding + line.length + " | " + line)
}else
Console.err.println("Please enter filename")
效果图
22 | import scala.io.Source
0 |
22 | if (args.length > 0) {
51 | for (line <- Source.fromFile(args(0)).getLines())
37 | println(line.length + " " + line)
1 | }
4 | else
46 | Console.err.println("Please enter filename")
以上就是最简单的函数式,去除var可变变量的依赖
利用函数第一公民的特点
定义一个叫做withPrintWriter的方法。用于解决Writer等资源close的问题。这种模式其实也叫作loan pattern。借贷模式,我把某个资源借给你用,你只要定义一个方法,我作为借贷方会提供你这个资源,并且我自己会确保在最后释放这个资源
def withPrintWriter(file: File, op: PrintWriter => Unit) = {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
使用的时候,就可以直接这么写
withPrintWriter(
new File("date.txt"),
writer => writer.println(new java.util.Date)
)
scala里为了更加美观的展示方法调用,当方法只有一个参数的时候,可以用花括号替代小括号
例如
println("Hello, world!")
可以写成
println { "Hello, world!" }
但是如果是
g.substring (7,9)
却不可以写成:
g.substring {7,9} //error
但是我们可以使用“分居参数”函数啊,把上面的例子改造下
def withPrintWriter(file: File)(op: PrintWriter => Unit) = {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
调用方就可以这么玩了!!!仿佛写了个方法
val file = new File("date.txt")
withPrintWriter(file) { writer =>
writer.println(new java.util.Date)
}
引申出来,如果我设计了这么一个自定义的assert,叫做myAssert
var assertionsEnabled = true
def myAssert(predicate: () => Boolean) =
if (assertionsEnabled && !predicate())
throw new AssertionError
我可以这么用
myAssert(() => 5 > 3)
就是有点别扭,于是,我们可以使用BY-NAME PARAMETERS 语法糖进一步简化
修改定义为
def byNameAssert(predicate: => Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
嘿,我们调用的时候就可以省掉参数部分了
byNameAssert(5 > 3)