functional scala no.1

2017-09-18  本文已影响8人  来福马斯特

通常我们作为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)
上一篇下一篇

猜你喜欢

热点阅读