Scala 用宏写入数组填充

Scala 用宏写入数组填充,scala,macros,Scala,Macros,我正在尝试使用scala宏编写一个函数,用给定的值填充数组。例如,呼叫: val ary = Array( 0, 1, 2 ) fill3( ary, 50+25 ) 应扩大到: val ary = Array(0, 1, 2 ) { val $value = 50+25 ary(0) = $value ary(1) = $value ary(2) = $value } 这是我的第一次尝试: def fill3( ary: Array[Int], x: Int

我正在尝试使用scala宏编写一个函数,用给定的值填充数组。例如,呼叫:

val ary = Array( 0, 1, 2 )
fill3( ary, 50+25 )
应扩大到:

val ary = Array(0, 1, 2 )
{
  val $value = 50+25
  ary(0) = $value
  ary(1) = $value
  ary(2) = $value       
}
这是我的第一次尝试:

def fill3( ary: Array[Int], x: Int ) = macro fill_impl3

def fill_impl3( c: Context )
( ary: c.Expr[Array[Int]], x: c.Expr[Int]): c.Expr[Unit] = {
  import c.universe._        
  def const(x:Int) = Literal(Constant(x))

  //Precompute x
  val valName = newTermName("$value")
  val valdef = ValDef( Modifiers(), valName, TypeTree(typeOf[Int]), x.tree )

  val updates = List.tabulate( 3 ){
  i => Apply( Select( ary.tree, "update"), List( const(i), ??? ) )
  }

  val insts = valdef :: updates
  c.Expr[Unit](Block(insts:_*))
}
但我在这里被困有两个原因:

  • 我不知道如何获取预计算的值(
    $value
  • 对于大小为3、4、6、9和27的数组,我需要几个这样的函数。有没有办法弄干定义,或者我应该写
    fill3
    fill4
    fill6
    ,等等
  • 有正确的方法进行吗?我如何解决我的两个问题


    编辑:我意识到我最初的问题很愚蠢,因为必须在编译时知道大小…

    您可以尝试使用
    具体化
    并打印其结果的原始树来解决它:

    def fill(size:Int, ary: Array[Int], x: Int ) = macro fill_impl
    
    def fill_impl( c: Context )
    (size:c.Expr[Int], ary: c.Expr[Array[Int]], x: c.Expr[Int]): c.Expr[Unit] = {
      import c.universe._        
      def const(x:Int) = Literal(Constant(x))
    
      val Literal(Constant(arySize:Int)) = size.tree
    
      //Precompute x
      val valName = newTermName("$value")
      val valdef = ValDef( Modifiers(), valName, TypeTree(typeOf[Int]), x.tree )
    
      val updates = List.tabulate( arySize ){
      i => Apply( Select( ary.tree, "update"), List( const(i), Ident(valName) ) )
      }
    
      val insts = valdef :: updates
      c.Expr[Unit](Block(insts:_*))
    }
    
    def fill_impl3( c: Context )
    ( ary: c.Expr[Array[Int]], x: c.Expr[Int]): c.Expr[Unit] = {
      import c.universe._
      val r = reify {
         val $value = x.splice
         val $arr  = ary.splice
         $arr(0)   = $value
         $arr(1)   = $value
         $arr(2)   = $value
      }
      println( showRaw( r.tree ))
      r
    }
    
    这就产生了

    val vt = newTermName("$value")
    val at = newTermName("$arr")
    val ut = newTermName("update")
    Block(List(
      ValDef(Modifiers(), vt, TypeTree(), ...),
      ValDef(Modifiers(), at, TypeTree(), ...),
      Apply(Select(Ident(at), ut), List(Literal(Constant(0)), Ident(vt))),
      Apply(Select(Ident(at), ut), List(Literal(Constant(1)), Ident(vt)))),
      Apply(Select(Ident(at), ut), List(Literal(Constant(2)), Ident(vt)))
    )
    

    数组的大小在编译时是已知的吗?我刚刚更新了这个问题以澄清这一点。我用固定的大小简化了这个问题。在这里使用宏有什么好处?值在编译时是已知的还是可以是任意表达式?@kimstebel 1。学习宏。2.我正在重写一个数值密集型代码,该代码在小数组(编译时已知大小)上使用大量计算。我想看看是否可以通过展开所有循环来提高速度,以避免计数器和绑定检查的增加。我计划用这种方式写向量运算。这很奇怪,我也在用M6。您的项目是否在某个地方可用?如何在不将其拆分为子项目的情况下对其进行编译?我将在子项目中拆分它,但目前我愚蠢地注释了main方法,当宏编译时,我将取消注释它…不要在标识符上使用
    $
    !它们是为编译器保留的,具体化已经可以防止标识符冲突。