Scala 部分应用程序和返回函数之间有区别吗?
就底层而言:堆栈/堆分配、垃圾收集、资源和性能,以下三者之间的区别是什么:Scala 部分应用程序和返回函数之间有区别吗?,scala,currying,partial-application,Scala,Currying,Partial Application,就底层而言:堆栈/堆分配、垃圾收集、资源和性能,以下三者之间的区别是什么: 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") 除了声明中
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
的额外调用):
生成以下纯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, String b) {
Predef..MODULE$.println(new Tuple2(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");
}
}
Do2
和Do3
向下编译为相同的字节码。区别只存在于@ScalaSignature
注释中
Do1
是直截了当的:立即应用返回的函数
Do2("a")("b")
Do2("a", "b");
使用Do2
,编译器发现这不是部分应用程序,并将其编译为单个方法调用
这里,首先部分应用
Do2
和Do3
,然后立即应用返回的函数
Do2("a")("b")
Do2("a", "b");
结论: 我想说,
Do2
和Do3
在生成的字节码中基本上是等价的。一个完整的应用程序产生一个简单、廉价的方法调用。部分应用程序在调用方生成匿名函数类。您使用的变体主要取决于您试图传达的意图
Do1
始终创建立即函数对象,但在被调用的代码中这样做。如果您希望大量执行函数的部分应用程序,那么使用此变量将减少代码大小,并且可能会更早地触发JIT编译器,因为调用同一代码的频率更高。完整的应用程序将更慢,至少在JIT编译器内联之前,并且随后在各个调用站点消除对象创建。我不是这方面的专家,所以我不知道你是否可以期待这种优化。我最好的猜测是,对于纯函数,您可以这样做。好吧,这与咖喱无关。无论如何,这个问题是关于秘密的。对不起,我忽略了你问题的一个重要部分:它与内存分配的关系。我仍然认为相关问题中有很多相关信息,但这不是重复的,你是对的。我猜在字节码级别上,Do2
和Do3
是相同的。您对Do2
的调用可能是一个简单的方法调用,而我希望Do3
调用创建一个中间函数对象Do1无论如何都应该这样做。也许现在有更多时间的人可以做一个javap
并写下来作为答案。非常感谢您的详细分析,尤其是结论。感谢您的时间和努力。@Alex不客气!再次抱歉,你一开始没抓住问题的重点。回答这个问题很有趣!
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: String) = { println(a, b) }
public void Do3(String a, String b) {
Predef.MODULE$.println(new Tuple2(a, b));
}
Do1("a")("b")
Do1("a").apply("b");
Do2("a")("b")
Do2("a", "b");
(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");