scala – 避免功能中的拳击/拆箱

对于数字密集型代码,我已经写了一个带有以下签名的函数

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,x: Double ) = if( i==0 || j ==0 ) 1.0 else 0.5
} )

是否有办法避免拳击/拆箱,同时仍然使用lambda语法?我希望宏将有所帮助,但我无法解决任何问题.

编辑:我用javap分析了function3生成的字节码.编译器根据通用方法(见下文)生成一个未装箱的方法.有没有办法直接打电话给未装箱的人?

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,D]):F3[A,D] = macro impl[A,D]

  def impl[A,D](c:Context)(f:c.Expr[Function3[A,D]]):c.Expr[F3[A,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()
                  ),TypeTree(),Block(
                    List(
                      Apply(
                        Select(Super(This(newTypeName("")),newTypeName("")),newTermName("<init>")),List()
                      )
                    ),Literal(Constant(()))
                  )
                ),DefDef(
                  Modifiers(Flag.OVERRIDE),newTermName("apply"),List(args),body
                )
              )
            )
          )
        ),Apply(
          Select(
            New(
              Ident(newTypeName("$anon"))
            ),nme.CONSTRUCTOR
          ),List()
        )
      )




    c.Expr[F3[A,D]](res)
  }

由于我将宏定义为隐式,所以可以这样使用:

def foo(f:F3[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,Double的特殊情况,但可以很容易地修复.我把这个作为一个练习给读者.

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...