2017-02-21 84 views
3

考慮到返回另一個功能的功能:是否可以強制早期評估咖喱功能?

def prepareFunction(args: List[Any]): String => Unit = { 
    println(s"Slow processing of $args...") 
    val results = args.map(a => s"processed $a") 

    def doSomething(s: String): Unit = { 
    println(s"Do something quick with $s and $results") 
    } 

    doSomething 
} 

這裏的想法是:一個外函數做一些繁重的處理,並返回一個使用在封閉範圍定義的變量一個內部函數:

val doSomethingWithArgs = prepareFunction(List("arg1", "arg2", 3)) 
    //> Slow processing of List(arg1, arg2, 3)... 
doSomethingWithArgs("abc") 
    //> Do something quick with abc and List(processed arg1, processed arg2, processed 3) 
doSomethingWithArgs("cde") 
    //> Do something quick with cde and List(processed arg1, processed arg2, processed 3) 

請注意,外部函數只被評估一次。

對於多個參數列表和Scala的Currying syntax我們可以寫類似的東西:

def prepareCurried(args: List[Any])(s: String): Unit = { 
    println(s"Slow processing of $args") 
    val results = args.map(a => s"processed $a") 

    def doSomething(s: String): Unit = { 
    println(s"Do something quick with $s and $results") 
    } 

    doSomething(s) 
} 

但「外」功能獲取評估每次:

val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _ 
doSomethingWithOtherArgs("abc") 
    //> Slow processing of List(4, 5, 6) 
    //> Do something quick with abc and List(processed 4, processed 5, processed 6) 
doSomethingWithOtherArgs("cde") 
    //> Slow processing of List(4, 5, 6) 
    //> Do something quick with cde and List(processed 4, processed 5, processed 6) 

我的問題是,我可以採用某種力量prepareCurried要在波紋管上進行評估?

val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _ 

換一種說法,是有可能得到相同的效果"evaluation on definition"當部分應用與多參數列表的功能?

+4

這就是curried函數應該如何按照定義行爲,我想不出爲什麼要改變它。你已經在你的模式1中有其他選擇。 –

+0

嘿@Sarvesh,沒有特別的理由,我只是想知道currying和返回函數的函數之間的對稱性。 –

回答

10

這是從Scala的反射API打破了reify可以得到怎樣的語法被脫的感覺是有幫助的情況下:

scala> import scala.reflect.runtime.universe.{ reify, showCode } 
import scala.reflect.runtime.universe.{reify, showCode} 

scala> showCode(reify(prepareCurried(List(4, 5, 6)) _).tree) 
res0: String = 
{ 
    val eta$0$1 = List.apply(4, 5, 6); 
    ((s) => $read.prepareCurried(eta$0$1)(s)) 
} 

有根本沒有辦法讓preparedCurried(eta)部分,而無需一個s。當您考慮如何在JVM上表示具有多個參數列表的方法時(即,所有參數列表被搗毀在一起)時,這是有意義的。正如上面的評論者所指出的那樣,如果你想能夠將第二個函數的準備與它的應用程序分開,你需要明確地返回一個函數。

+0

有趣。出於好奇,我嘗試了'showCode(reify(List(1,2,3).foldLeft(0)_)。tree)'with analogue results('((op)=> eta $ 0 $ 1.foldLeft(0) (OP))')。有道理,所以,我們真正得到的是圍繞原始功能的「包裝」功能? 'prepareCurried _'實際上是'((args)=>((s)=> prepareCurried(args)(s)))''。 –

+1

@AnthonyAccioly正確,但在Function1的意義上並沒有真正的「原始函數」--prepareCurried _'只是解析匿名函數定義的語法。 –

2

我認爲如果我們將prepareFunction的內部寫成函數文字而不是defs,會更容易看到發生了什麼。

這將是你的方法最初看起來像:

def prepareFunction(args: List[Any]): String => Unit = { 
    println(s"Slow processing of $args...") 
    val results = args.map(a => s"processed $a") 

    (s: String) => println(s"Do something quick with $s and $results") 
} 

這是「相當於」你令行禁止方法:

def prepareCurried(args: List[Any]): Unit = (s: String) => { 
    println(s"Slow processing of $args") 
    val results = args.map(a => s"processed $a") 

    println(s"Do something quick with $s and $results") 
} 

現在可以很容易地看到,在prepareCurried處理緩慢是正在返回的函數的一部分。

你無能爲力。在使用多個參數列表而不是明確寫入函數時,這是您失去的靈活性。您必須選擇更適合的每個用例。