scala – 部分应用和返回函数之间有区别吗?
在引擎盖方面:堆栈/堆分配,垃圾收集,资源和性能,以下三个有什么区别:
def Do1(a:String) = { (b:String) => { println(a,b) }} def Do2(a:String)(b:String) = { println(a,b) } def Do3(a:String,b:String) = { println(a,b) } Do1("a")("b") Do2("a")("b") (Do3("a",_:String))("b") 除了声明中每个采取和返回多少参数的明显表面差异 解决方法
反编译以下类(请注意与您的问题相比对Do2的额外调用):
class Test { def Do1(a: String) = { (b: String) => { println(a,b) } } def Do2(a: String)(b: String) = { println(a,b) } def Do3(a: String,b: String) = { println(a,b) } Do1("a")("b") Do2("a")("b") (Do2("a") _)("b") (Do3("a",_: String))("b") } 产生这个纯Java代码: public class Test { public Function1<String,BoxedUnit> Do1(final String a) { new AbstractFunction1() { public final void apply(String b) { Predef..MODULE$.println(new Tuple2(a,b)); } }; } public void Do2(String a,String b) { Predef..MODULE$.println(new Tuple2(a,b)); } public void Do3(String a,b)); } public Test() { Do1("a").apply("b"); Do2("a","b"); new AbstractFunction1() { public final void apply(String b) { Test.this.Do2("a",b); } }.apply("b"); new AbstractFunction1() { public final void apply(String x$1) { Test.this.Do3("a",x$1); } }.apply("b"); } } (此代码无法编译,但足以进行分析) 让我们逐个看一下(每个列表中的Scala& Java): def Do1(a: String) = { (b: String) => { println(a,b) } } public Function1<String,BoxedUnit> Do1(final String a) { new AbstractFunction1() { public final void apply(String b) { Predef.MODULE$.println(new Tuple2(a,b)); } }; } 无论如何调用Do1,都会创建一个新的Function对象. def Do2(a: String)(b: String) = { println(a,b) } public void Do2(String a,String b) { Predef.MODULE$.println(new Tuple2(a,b)); } def Do3(a: String,b) } public void Do3(String a,b)); } Do2和Do3编译为相同的字节码.区别仅在于@ScalaSignature注释. Do1("a")("b") Do1("a").apply("b"); Do1是直接的:立即应用返回的函数. Do2("a")("b") Do2("a","b"); 使用Do2,编译器会发现这不是部分应用程序,并将其编译为单个方法调用. (Do2("a") _)("b") new AbstractFunction1() { public final void apply(String b) { Test.this.Do2("a",b); } }.apply("b"); (Do3("a",_: String))("b") new AbstractFunction1() { public final void apply(String x$1) { Test.this.Do3("a",x$1); } }.apply("b"); 这里,首先部分应用Do2和Do3,然后立即应用返回的函数. 结论: 我想说Do2和Do3在生成的字节码中大部分都是等价的.完整的应用程序会导致简单,廉价的方法调用.部分应用程序在调用者处生成匿名函数类.您使用的变体主要取决于您尝试沟通的意图. Do1总是创建一个立即的函数对象,但是在被调用的代码中这样做.如果您希望对该函数进行大量部分应用,那么使用此变体将减少您的代码大小,并可能更早地触发JIT-Compiler,因为更频繁地调用相同的代码.完整的应用程序将会更慢,至少在JIT-Compiler内联之前并且随后消除了各个呼叫站点的对象创建.我不是这方面的专家,所以我不知道你是否可以期待那种优化.对于纯粹的功能,我最好的猜测就是你可以. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |