2010-04-11 93 views
7

以下方法有什麼問題?在可變參數中使用延遲評估函數

def someMethod(funcs: => Option[String]*) = { 
... 
} 
+0

正式參數'funcs'的名字是高度可疑的。用thunk實現的名稱參數不是公開的函數。 – 2010-04-11 22:42:12

+0

他們也不是通常所說的懶惰評價的論點。即,如果多次使用該值,則作爲實際參數給出的表達式將被多次評估。這會產生性能上的後果,如果這種表達有副作用,它們也會發生繁殖。 – 2010-04-12 04:05:42

+0

這可能是一個解決方案:http://stackoverflow.com/a/34373967/2825964 – 2015-12-19 18:36:45

回答

5

如果你加括號這實際上「作品」 2.7.7下:

scala> def someMethod(funcs: => (Option[String]*)) = funcs 
someMethod: (=> Option[String]*)Option[String]* 

但它實際上並沒有在運行時的工作:

scala> someMethod(Some("Fish"),None) 
    scala.MatchError: Some(Fish) 
at scala.runtime.ScalaRunTime$.boxArray(ScalaRunTime.scala:136) 
at .someMethod(<console>:4) 
at .<init>(<console>:6) 
at .<clinit>(<console>) ... 

在2.8拒不讓您指定X *作爲任何函數或名稱參數的輸出,即使您可以將其指定爲輸入(這是r21230,post-Beta 1):

scala> var f: (Option[Int]*) => Int = _ 
f: (Option[Int]*) => Int = null 

scala> var f: (Option[Int]*) => (Option[Int]*) = _ 
<console>:1: error: no * parameter type allowed here 
     var f: (Option[Int]*) => (Option[Int]*) = _ 

但是,如果你試圖從一個方法轉換,它的工作原理:

scala> def m(oi: Option[Int]*) = oi 
m: (oi: Option[Int]*)Option[Int]* 

scala> var f = (m _) 
f: (Option[Int]*) => Option[Int]* = <function1> 

scala> f(Some(1),None) 
res0: Option[Int]* = WrappedArray(Some(1), None) 

所以這並不完全一致。

在任何情況下,你都不可能達到你想要的東西通過傳遞一個數組,然後發送該數組的東西,需要反覆論證:

scala> def aMethod(os: Option[String]*) { os.foreach(println) } 
aMethod: (os: Option[String]*)Unit 

scala> def someMethod(funcs: => Array[Option[String]]) { aMethod(funcs:_*) } 
someMethod: (funcs: => Array[Option[String]])Unit 

scala> someMethod(Array(Some("Hello"),Some("there"),None)) 
Some(Hello) 
Some(there) 
None 

如果你真的想(容易)通過了一堆然後你需要一點點的基礎設施,據我所知在庫中不存在很好的結構(這是2.8的代碼;將它看作2.7中類似策略的靈感):

class Lazy[+T](t:() => T, lt: Lazy[T]) { 
    val params: List[() => T] = (if (lt eq null) Nil else t :: lt.params) 
    def ~[S >: T](s: => S) = new Lazy[S](s _,this) 
} 
object Lz extends Lazy[Nothing](null,null) { 
    implicit def lazy2params[T : Manifest](lz: Lazy[T]) = lz.params.reverse.toArray 
} 

現在你可以很容易地創建一堆參數那些被懶惰評估的評估者:

scala> import Lz._ // To get implicit def 
import Lz._ 

scala> def lazyAdder(ff: Array[()=>Int]) = { 
    | println("I'm adding now!"); 
    | (0 /: ff){(n,f) => n+f()} 
    | } 
lazyAdder: (ff: Array[() => Int])Int 

scala> def yelp = { println("You evaluated me!"); 5 } 
yelp: Int 

scala> val a = 3 
a: Int = 3 

scala> var b = 7 
b: Int = 7 

scala> lazyAdder(Lz ~ yelp ~ (a+b)) 
I'm adding now! 
You evaluated me! 
res0: Int = 15 

scala> val plist = Lz ~ yelp ~ (a+b) 
plist: Lazy[Int] = [email protected]ee1775 

scala> b = 1 
b: Int = 1 

scala> lazyAdder(plist) 
I'm adding now! 
You evaluated me! 
res1: Int = 9 
3

明顯重複的參數不可用於名稱參數。

+0

但是,只是踢,試試這個:'def f(oi:Option [Int] *)= oi'在REPL中。有趣的,呃? – 2010-04-12 01:58:50

+0

@Rex_Kerr:我想。你指的是什麼有趣的事情? – 2010-04-12 03:44:18

+1

您可以從方法返回Option [Int] *,並且可以使用(f _)使其成爲函數。但是嘗試找到允許您表示類型的語法。所以我不清楚重複參數是否不可用於名稱參數,或者語法是否不允許您表達您想要的類型(或兩者)。 – 2010-04-12 05:31:16