对于数字密集型代码我写具有以下签名的函数:
def update( f: (Int,Int,Double) => Double ): Unit = {...}
然而,由于Function3
不专业,每一个应用f
装箱/拆箱结果的3个参数和结果类型。
我可以用一个特殊的更新类:
trait Updater {
def apply( i: Int, j: Int, x: Double ): Double
}
def update( f: Updater ): Unit = {...}
但调用是繁琐(和java-ISH):
//with function
b.update( (i,j,_) => if( i==0 || j ==0 ) 1.0 else 0.5 )
//with updater
b.update( new Updater {
def apply( i: Int, j: Int, x: Double ) = if( i==0 || j ==0 ) 1.0 else 0.5
} )
有没有办法来避免装箱/拆箱同时仍使用lambda语法? 我希望宏会有所帮助,但我想不出任何解决方案。
编辑:我分析了javap的功能3生成的字节码。 由编译器产生的装箱方法,沿通用方法(见下文)。 有没有一种方法可以直接调用拆箱一个?
public final double apply(int, int, double);
Code:
0: ldc2_w #14; //double 100.0d
3: iload_2
4: i2d
5: dmul
6: iload_1
7: i2d
8: ddiv
9: dreturn
public final java.lang.Object apply(java.lang.Object, java.lang.Object, java.lang.Object);
Code:
0: aload_0
1: aload_1
2: invokestatic #31; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
5: aload_2
6: invokestatic #31; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
9: aload_3
10: invokestatic #35; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
13: invokevirtual #37; //Method apply:(IID)D
16: invokestatic #41; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
19: areturn
既然你提到宏作为一个可能的解决方案,我写了一个宏以匿名函数的思想,提取申请方法并将其插入到扩展的自定义功能特性叫做匿名类F3
。 这是相当长的实现。
性状F3
trait F3[@specialized A, @specialized B, @specialized C, @specialized D] {
def apply(a:A, b:B, c:C):D
}
宏
implicit def function3toF3[A,B,C,D](f:Function3[A,B,C,D]):F3[A,B,C,D] = macro impl[A,B,C,D]
def impl[A,B,C,D](c:Context)(f:c.Expr[Function3[A,B,C,D]]):c.Expr[F3[A,B,C,D]] = {
import c.universe._
var Function(args,body) = f.tree
args = args.map(c.resetAllAttrs(_).asInstanceOf[ValDef])
body = c.resetAllAttrs(body)
val res =
Block(
List(
ClassDef(
Modifiers(Flag.FINAL),
newTypeName("$anon"),
List(),
Template(
List(
AppliedTypeTree(Ident(c.mirror.staticClass("mcro.F3")),
List(
Ident(c.mirror.staticClass("scala.Int")),
Ident(c.mirror.staticClass("scala.Int")),
Ident(c.mirror.staticClass("scala.Double")),
Ident(c.mirror.staticClass("scala.Double"))
)
)
),
emptyValDef,
List(
DefDef(
Modifiers(),
nme.CONSTRUCTOR,
List(),
List(
List()
),
TypeTree(),
Block(
List(
Apply(
Select(Super(This(newTypeName("")), newTypeName("")), newTermName("<init>")),
List()
)
),
Literal(Constant(()))
)
),
DefDef(
Modifiers(Flag.OVERRIDE),
newTermName("apply"),
List(),
List(args),
TypeTree(),
body
)
)
)
)
),
Apply(
Select(
New(
Ident(newTypeName("$anon"))
),
nme.CONSTRUCTOR
),
List()
)
)
c.Expr[F3[A,B,C,D]](res)
}
由于我所定义的宏为隐式的,它可用于这样的:
def foo(f:F3[Int,Int,Double,Double]) = {
println(f.apply(1,2,3))
}
foo((a:Int,b:Int,c:Double)=>a+b+c)
FOO被调用之前,调用宏时,因为foo
预期的一个实例F3
。 正如预期的那样,在调用foo
版画“6.0”。 现在让我们来看看的拆卸foo
方法,以确保没有装箱/拆箱发生:
public void foo(mcro.F3);
Code:
Stack=6, Locals=2, Args_size=2
0: getstatic #19; //Field scala/Predef$.MODULE$:Lscala/Predef$;
3: aload_1
4: iconst_1
5: iconst_2
6: ldc2_w #20; //double 3.0d
9: invokeinterface #27, 5; //InterfaceMethod mcro/F3.apply$mcIIDD$sp:(IID)D
14: invokestatic #33; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
17: invokevirtual #37; //Method scala/Predef$.println:(Ljava/lang/Object;)V
20: return
这都是在这里完成的唯一的拳击是调用println
。 好极了!
最后一个说明:在当前状态下,宏只适用于特殊情况下Int,Int,Double,Double
,但可以很容易地固定。 我将它作为一个练习留给读者。
关于延伸匿名类Function3
通过Scalac产生(其中显示您的字节码) -这是无法调用重载apply
从内部原始参数b.update
,因为update
方法需要一个Function3
,不具有一个apply
与原语的参数。
从内部Function3
字节码,唯一的apply
是:
public abstract java.lang.Object apply(java.lang.Object, java.lang.Object, java.lang.Object);
你也可以使用Function2[Long, Double]
,这是专门对这些类型和编码2的整数x
和y
在只要x.toLong << 32 + y
,并把它们作为解码v & 0xffffffff
和(v >> 32) & 0xffffffff
。
由于Function1
是专业化,一个可能的解决方案是使用钻营,改变你的更新方法:
def update( f: Int => Int => Double => Double ): Unit = {...}
并相应地改变内联函数。 你的榜样( update
稍微修改,以快速测试吧):
scala> def update( f: Int => Int => Double => Double ): Double = f(1)(2)(3.0)
update: (f: Int => (Int => (Double => Double)))Double
scala> update(i => j => _ => if (i == 0 && j == 0) 1.0 else 0.5)
res1: Double = 0.5
编辑:日月光评论解释,它并不能完全帮助,因为第一个参数仍然是盒装。 我离开了答案,以保持一个跟踪它。