![Scala编程(第4版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/353/38381353/b_38381353.jpg)
9.4 编写新的控制结构
在拥有一等函数的语言中,可以有效地制作出新的控制接口,尽管语言的语法是固定的。你需要做的就是创建接收函数作为入参的方法。
例如下面这个“twice”控制结构,它重复某个操作两次,并返回结果:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-206-1.jpg?sign=1739283843-87qVSsYEVEUYDzlDfxWOWgI5YPxs9tmh-0-2b7b68c536eeabce9a9f97ee7054bc38)
本例中的op类型为Double => Double,意思是这是一个接收一个Double作为入参,返回另一个Double的函数。
每当你发现某个控制模式在代码中多处出现时,就应该考虑将这个模式实现为新的控制结构。在本章前面的部分看到了filesMatching这个非常特殊的控制模式,现在来看一个更加常用的编码模式:打开某个资源,对它进行操作,然后关闭这个资源。可以用类似如下的方法,将这个模式捕获成一个控制抽象:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-206-2.jpg?sign=1739283843-D36v61wArmvCyVnSptpEO2Zt6jQCuRLK-0-66a0289f49cf1b39914d5a93a0009800)
有了这个方法后,你就可以像这样来使用它:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-206-3.jpg?sign=1739283843-S09uqEGIJgkwi2TERC1igfqIur7L2Grf-0-29723df4dc5a1ee17170ae4d95e8276e)
使用这个方法的好处是,确保文件在最后被关闭的是withPrintWriter而不是用户代码。因此不可能出现使用者忘记关闭文件的情况。这个技巧被称作贷出模式(loan pattern),因为是某个控制抽象函数,比如withPrintWriter,打开某个资源并将这个资源“贷出”给函数。例如,前一例中的withPrintWriter将一个PrintWriter“贷出”给函数op。当函数完成时,它会表明自己不再需要这个“贷入”的资源。这时这个资源就在finally代码块中被关闭了,这样能确保不论函数是正常返回还是抛出异常,资源都会被正常关闭。
可以用花括号而不是圆括号来表示参数列表,这样调用方的代码看上去更像是在使用内建的控制结构。在Scala中,只要有那种只传入一个参数的方法调用,都可以选择使用花括号来将入参包起来,而不是圆括号。
例如,可以不这样写:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-207-1.jpg?sign=1739283843-VzS4XcmiS2j2z3INz5fH6nBpwjsskRYS-0-ddd19294da8d0563e64bc573217f75af)
而是写成:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-207-2.jpg?sign=1739283843-cakT8TW9DrlY3muz5kheCnwQ0qWUyre6-0-25c96ae9d11c967aa3f3a1664b0d0723)
在第二个例子中,用了花括号而不是圆括号来将println的入参包起来。不过,这个花括号技巧仅对传入单个入参的场景适用。参考下面这个尝试打破这个规则的例子:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-207-3.jpg?sign=1739283843-oBQLKDQHiB3tApo5W7MBkfBm8kiB02DE-0-70a9569d205d6a3a40d8aaa1f5e04940)
由于你尝试传入两个入参给substring,当试着将这些入参用花括号包起来时,会得到一个错误提示。这个时候需要使用圆括号:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-208-1.jpg?sign=1739283843-IgsTuJuio2w9N9cUlJoGVDivgf3DVkGe-0-723def90eb08893b4afc8abba72ed13a)
Scala允许用花括号替代圆括号来传入单个入参的目的是,为了让调用方程序员在花括号当中编写函数字面量。这能让方法用起来更像是控制抽象。拿前面的withPrintWriter举例,在最新的版本中,withPrintWriter接收两个入参,因此你不能用花括号。尽管如此,由于传入withPrintWriter的函数是参数列表中的最后一个,可以用柯里化将第一个File参数单独拉到一个参数列表中,这样剩下的函数就独占了第二个参数列表。示例9.4展示了如何重新定义withPrintWriter。
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-208-2.jpg?sign=1739283843-EAalpYduyqQF8pThnad7sRCAudZbzkHq-0-a65269ca86249e2a9bcd93d586e726ce)
示例9.4 用贷出模式写入文件
新版本跟老版本的唯一区别在于现在有两个各包含一个参数的参数列表,而不是一个包含两个参数的参数列表。仔细看两个参数之间的部分,在前一个版本的withPrintWriter中(173页),你看到的是...File, op...,而在新的版本中,你看到的是...File)(op...。有了这样的定义,你就可以用更舒服的语法来调用这个方法了:
![](https://epubservercos.yuewen.com/E8DF3B/20205397808551606/epubprivate/OEBPS/Images/40272-00-208-3.jpg?sign=1739283843-edEQDGtiSzBJuOpCMqGeKt8vaynzHtvv-0-a5e00211ac00226f8c0e31fcf6ac2b3d)
在本例中,第一个参数列表,也就是那个包含了一个File入参的参数列表,用的是圆括号。而第二个参数列表,即包含函数入参的那个,用的是花括号。