Ответ 1
На уровне байт-кода
def sumDoubles(n: Int): Int = {
def dbl(a: Int) = 2 * a;
if(n > 0)
dbl(n) + sumDoubles(n - 1)
else
0
}
точно совпадает с
private[this] def dbl(a: Int) = 2 * a;
def sumDoubles(n: Int): Int = {
if(n > 0)
dbl(n) + sumDoubles(n - 1)
else
0
}
Но не верьте мне на слово.
~/test$ javap -private -c Foo Compiled from "test.scala" public class Foo extends java.lang.Object implements scala.ScalaObject{ public Foo(); Code: 0: aload_0 1: invokespecial #10; //Method java/lang/Object."":()V 4: return private final int dbl$1(int); Code: 0: iconst_2 1: iload_1 2: imul 3: ireturn public int sumDoubles(int); Code: 0: iload_1 1: iconst_0 2: if_icmple 21 5: aload_0 6: iload_1 7: invokespecial #22; //Method dbl$1:(I)I 10: aload_0 11: iload_1 12: iconst_1 13: isub 14: invokevirtual #24; //Method sumDoubles:(I)I 17: iadd 18: goto 22 21: iconst_0 22: ireturn }
Если внутренняя функция фиксирует неизменяемую переменную, то есть перевод. Этот код
def foo(n: Int): Int = {
def dbl(a: Int) = a * n;
if(n > 0)
dbl(n) + foo(n - 1)
else
0
}
Переведено в
private[this] def dbl(a: Int, n: Int) = a * n;
def foo(n: Int): Int = {
if(n > 0)
dbl(n, n) + foo(n - 1)
else
0
}
Опять же, инструменты для вас
~/test$ javap -private -c Foo Compiled from "test.scala" public class Foo extends java.lang.Object implements scala.ScalaObject{ public Foo(); Code: 0: aload_0 1: invokespecial #10; //Method java/lang/Object."":()V 4: return private final int dbl$1(int, int); Code: 0: iload_1 1: iload_2 2: imul 3: ireturn public int foo(int); Code: 0: iload_1 1: iconst_0 2: if_icmple 22 5: aload_0 6: iload_1 7: iload_1 8: invokespecial #23; //Method dbl$1:(II)I 11: aload_0 12: iload_1 13: iconst_1 14: isub 15: invokevirtual #25; //Method foo:(I)I 18: iadd 19: goto 23 22: iconst_0 23: ireturn }
Если изменена изменяемая переменная, она должна быть в коробке, которая может быть более дорогой.
def bar(_n : Int) : Int = {
var n = _n
def subtract() = n = n - 1
if (n > 0) {
subtract
n
}
else
0
}
Переводит что-то вроде
private[this] def subtract(n : IntRef]) = n.value = n.value - 1
def bar(_n : Int) : Int = {
var n = _n
if (n > 0) {
val nRef = IntRef(n)
subtract(nRef)
n = nRef.get()
n
}
else
0
}
~/test$ javap -private -c Foo Compiled from "test.scala" public class Foo extends java.lang.Object implements scala.ScalaObject{ public Foo(); Code: 0: aload_0 1: invokespecial #10; //Method java/lang/Object."":()V 4: return private final void subtract$1(scala.runtime.IntRef); Code: 0: aload_1 1: aload_1 2: getfield #18; //Field scala/runtime/IntRef.elem:I 5: iconst_1 6: isub 7: putfield #18; //Field scala/runtime/IntRef.elem:I 10: return public int bar(int); Code: 0: new #14; //class scala/runtime/IntRef 3: dup 4: iload_1 5: invokespecial #23; //Method scala/runtime/IntRef."":(I)V 8: astore_2 9: aload_2 10: getfield #18; //Field scala/runtime/IntRef.elem:I 13: iconst_0 14: if_icmple 29 17: aload_0 18: aload_2 19: invokespecial #27; //Method subtract$1:(Lscala/runtime/IntRef;)V 22: aload_2 23: getfield #18; //Field scala/runtime/IntRef.elem:I 26: goto 30 29: iconst_0 30: ireturn }
Изменить: добавление функций первого класса
Чтобы получить распределение объектов, вам нужно использовать функции более первого класса
def sumWithFunction(n : Int, f : Int => Int) : Int = {
if(n > 0)
f(n) + sumWithFunction(n - 1, f)
else
0
}
def sumDoubles(n: Int) : Int = {
def dbl(a: Int) = 2 * a
sumWithFunction(n, dbl)
}
Это создает нечто вроде
def sumWithFunction(n : Int, f : Int => Int) : Int = {
if(n > 0)
f(n) + sumWithFunction(n - 1, f)
else
0
}
private[this] def dbl(a: Int) = 2 * a
def sumDoubles(n: Int) : Int = {
sumWithFunction(n, new Function0[Int,Int] {
def apply(x : Int) = dbl(x)
})
}
Здесь байтовый код
~/test$ javap -private -c Foo Compiled from "test.scala" public class Foo extends java.lang.Object implements scala.ScalaObject{ public Foo(); Code: 0: aload_0 1: invokespecial #10; //Method java/lang/Object."":()V 4: return public final int dbl$1(int); Code: 0: iconst_2 1: iload_1 2: imul 3: ireturn public int sumDoubles(int); Code: 0: aload_0 1: iload_1 2: new #20; //class Foo$$anonfun$sumDoubles$1 5: dup 6: aload_0 7: invokespecial #23; //Method Foo$$anonfun$sumDoubles$1."":(LFoo;)V 10: invokevirtual #29; //Method sumWithFunction:(ILscala/Function1;)I 13: ireturn public int sumWithFunction(int, scala.Function1); Code: 0: iload_1 1: iconst_0 2: if_icmple 30 5: aload_2 6: iload_1 7: invokestatic #36; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; 10: invokeinterface #42, 2; //InterfaceMethod scala/Function1.apply:(Ljava/lang/Object;)Ljava/lang/Object; 15: invokestatic #46; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 18: aload_0 19: iload_1 20: iconst_1 21: isub 22: aload_2 23: invokevirtual #29; //Method sumWithFunction:(ILscala/Function1;)I 26: iadd 27: goto 31 30: iconst_0 31: ireturn } ~/test$ javap -private -c "Foo\$\$anonfun\$sumDoubles\$1" Compiled from "test.scala" public final class Foo$$anonfun$sumDoubles$1 extends java.lang.Object implements scala.Function1,scala.ScalaObject,java.io.Serializable{ private final Foo $outer; public Foo$$anonfun$sumDoubles$1(Foo); Code: 0: aload_1 1: ifnonnull 12 4: new #10; //class java/lang/NullPointerException 7: dup 8: invokespecial #13; //Method java/lang/NullPointerException."":()V 11: athrow 12: aload_0 13: aload_1 14: putfield #17; //Field $outer:LFoo; 17: aload_0 18: invokespecial #20; //Method java/lang/Object."":()V 21: aload_0 22: invokestatic #26; //Method scala/Function1$class.$init$:(Lscala/Function1;)V 25: return public final java.lang.Object apply(java.lang.Object); Code: 0: aload_0 1: getfield #17; //Field $outer:LFoo; 4: astore_2 5: aload_0 6: aload_1 7: invokestatic #37; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 10: invokevirtual #40; //Method apply:(I)I 13: invokestatic #44; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; 16: areturn public final int apply(int); Code: 0: aload_0 1: getfield #17; //Field $outer:LFoo; 4: astore_2 5: aload_0 6: getfield #17; //Field $outer:LFoo; 9: iload_1 10: invokevirtual #51; //Method Foo.dbl$1:(I)I 13: ireturn public scala.Function1 andThen(scala.Function1); Code: 0: aload_0 1: aload_1 2: invokestatic #56; //Method scala/Function1$class.andThen:(Lscala/Function1;Lscala/Function1;)Lscala/Function1; 5: areturn public scala.Function1 compose(scala.Function1); Code: 0: aload_0 1: aload_1 2: invokestatic #60; //Method scala/Function1$class.compose:(Lscala/Function1;Lscala/Function1;)Lscala/Function1; 5: areturn public java.lang.String toString(); Code: 0: aload_0 1: invokestatic #65; //Method scala/Function1$class.toString:(Lscala/Function1;)Ljava/lang/String; 4: areturn }
Анонимный класс получает много кода, скопированного из свойства Function1. У этого есть стоимость с точки зрения нагрузки на класс, но не влияет на стоимость выделения объекта или выполнения кода. Другая стоимость - бокс и распаковка целого числа. Там надеюсь, что эта стоимость уйдет с 2.8 @специализированной аннотацией.