gpt4 book ai didi

scala - 避免在函数内装箱/拆箱

转载 作者:行者123 更新时间:2023-12-03 23:14:19 26 4
gpt4 key购买 nike

对于数字密集型代码,我编写了一个具有以下签名的函数:

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分析了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,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 的特殊情况。但这很容易解决。我把它作为练习留给读者。

关于scala - 避免在函数内装箱/拆箱,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12087259/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com