W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
上篇我們使用柯里化函數(shù)定義一個(gè)控制機(jī)構(gòu) withPrintWriter,它使用時(shí)語法調(diào)用有如 Scala 內(nèi)置的控制結(jié)構(gòu):
val file = new File("date.txt")
withPrintWriter(file){
writer => writer.println(new java.util.Date)
}
不過仔細(xì)看一看這段代碼,它和 scala 內(nèi)置的 if 或 while 表達(dá)式還是有些區(qū)別的,withPrintWrite r的{}中的函數(shù)是帶參數(shù)的含有“writer=>”。 如果你想讓它完全和 if 和 while 的語法一致,在 Scala 中可以使用傳民參數(shù)來解決這個(gè)問題。
注
:我們知道通常函數(shù)參數(shù)傳遞的兩種模式,一是傳值,一是引用。而這里是第三種按名稱傳遞。
下面我們以一個(gè)具體的例子來說明傳名參數(shù)的用法:
var assertionsEnabled=true
def myAssert(predicate: () => Boolean ) =
if(assertionsEnabled && !predicate())
throw new AssertionError
這個(gè) myAssert 函數(shù)的參數(shù)為一個(gè)函數(shù)類型,如果標(biāo)志 assertionsEnabled 為 True 時(shí),mymyAssert 根據(jù) predicate 的真假?zèng)Q定是否拋出異常,如果 assertionsEnabled 為 false,則這個(gè)函數(shù)什么也不做。
這個(gè)定義沒什么問題,但調(diào)用起來看起來卻有些別扭,比如:
myAssert(() => 5 >3 )
還需要 ()=>,你可以希望直接使用 5>3,但此時(shí)會(huì)報(bào)錯(cuò):
scala> myAssert(5 >3 )
<console>:10: error: type mismatch;
found : Boolean(true)
required: () => Boolean
myAssert(5 >3 )
此時(shí),我們可以把按值傳遞(上面使用的是按值傳遞,傳遞的是函數(shù)類型的值)參數(shù)修改為按名稱傳遞的參數(shù),修改方法,是使用=>開始而不是 ()=>來定義函數(shù)類型,如下:
def myNameAssert(predicate: => Boolean ) =
if(assertionsEnabled && !predicate)
throw new AssertionError
此時(shí)你就可以直接使用下面的語法來調(diào)用 myNameAssert:
myNameAssert(5>3)
此時(shí)就和 Scala 內(nèi)置控制結(jié)構(gòu)一樣了,看到這里,你可能會(huì)想我為什么不直接把參數(shù)類型定義為 Boolean,比如:
def boolAssert(predicate: Boolean ) =
if(assertionsEnabled && !predicate)
throw new AssertionError
調(diào)用也可以使用
boolAssert(5>3)
和 myNameAssert 調(diào)用看起來也沒什么區(qū)別,其實(shí)兩者有著本質(zhì)的區(qū)別,一個(gè)是傳值參數(shù),一個(gè)是傳名參數(shù),在調(diào)用 boolAssert(5>3)時(shí),5>3 是已經(jīng)計(jì)算出為 true,然后傳遞給 boolAssert 方法,而 myNameAssert(5>3),表達(dá)式 5>3 沒有事先計(jì)算好傳遞給 myNameAssert,而是先創(chuàng)建一個(gè)函數(shù)類型的參數(shù)值,這個(gè)函數(shù)的 apply 方法將計(jì)算5>3,然后這個(gè)函數(shù)類型的值作為參數(shù)傳給 myNameAssert。
因此這兩個(gè)函數(shù)一個(gè)明顯的區(qū)別是,如果設(shè)置 assertionsEnabled 為 false,然后試圖計(jì)算 x/0 ==0,
scala> assertionsEnabled=false
assertionsEnabled: Boolean = false
scala> val x = 5
x: Int = 5
scala> boolAssert ( x /0 ==0)
java.lang.ArithmeticException: / by zero
... 32 elided
scala> myNameAssert ( x / 0 ==0)
可以看到 boolAssert 拋出 java.lang.ArithmeticException: / by zero 異常,這是因?yàn)檫@是個(gè)傳值參數(shù),首先計(jì)算 x /0 ,而拋出異常,而 myNameAssert 沒有任何顯示,這是因?yàn)檫@是個(gè)傳名參數(shù),傳入的是一個(gè)函數(shù)類型的值,不會(huì)先計(jì)算 x /0 ==0,而在 myNameAssert 函數(shù)體內(nèi),由于 assertionsEnabled 為 false,傳入的 predicate 沒有必要計(jì)算(短路計(jì)算),因此什么也不會(huì)打印。如果我們把 myNameAssert 修改下,把 predicate 放在前面:
scala> def myNameAssert1(predicate: => Boolean ) =
| if( !predicate && assertionsEnabled )
| throw new AssertionError
myNameAssert1: (predicate: => Boolean)Unit
scala> myNameAssert1 ( x/0 ==0)
java.lang.ArithmeticException: / by zero
at $anonfun$1.apply$mcZ$sp(<console>:11)
at .myNameAssert1(<console>:9)
... 32 elided
這個(gè)傳名參數(shù)函數(shù)也拋出異常(你可以想想是為什么?)
前面的 withPrintWriter 我們暫時(shí)沒法使用傳名參數(shù),去掉 writer=>,否則就難以實(shí)現(xiàn)“租賃模式”,不過我們可以看看下面的例子,設(shè)計(jì)一個(gè) withHelloWorld 控制結(jié)構(gòu),這個(gè) withHelloWorld 總打印一個(gè)“hello,world”
import scala.io._
import java.io._
def withHelloWorld ( op: => Unit) {
op
println("Hello,world")
}
val file = new File("date.txt")
withHelloWorld{
val writer=new PrintWriter(file)
try{
writer.println(new java.util.Date)
}finally{
writer.close()
}
}
withHelloWorld {
println ("Hello,Guidebee")
}
Hello,world
Hello,Guidebee
Hello,world
可以看到 withHelloWorld 的調(diào)用語法和 Scala 內(nèi)置控制結(jié)構(gòu)非常象了。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: