【问题标题】:Find all possible Sequences creation in a tree using Scala macros使用 Scala 宏在树中查找所有可能的序列创建
【发布时间】:2013-07-31 12:02:37
【问题描述】:

我想在树中找到所有可能的序列创建,使用 Scala 宏。

val l = List(1, 2)
val v = Vector(1, 2)
val ab = ArrayBuffer(1, 2)
val s = Seq(1, 2)

但以下匹配不起作用:

case Apply(TypeApply(Select(path, Name("apply")), _), args) if path.tpe <:< weakTypeOf[SeqFactory[Any]] => ...

同样,我需要在一个序列上按索引查找所有访问:

val v = Vector(1, 2)
val one = v(0)

或所有 'apply' 方法在路径上调用,其中 path.tpe <:>

如何写这张支票? 这不编译:

case Apply(Select(path, Name("apply")), List(idx)) if path.tpe <:< typeOf[Seq[_]]

【问题讨论】:

    标签: scala scala-2.10 scala-macros


    【解决方案1】:

    我不确定您到底尝试了什么,但乍一看似乎您至少缺少两部分:您需要使用 Traverser 来遍历树的所有后代,并且您需要对每个候选树进行类型检查,以确保它被脱糖到你可以告诉你有一个SeqFactory 的应用程序。

    例如,这是一个在编译时打印类中所有序列创建树的快速实现:

    import scala.language.experimental.macros
    import scala.reflect.macros.Context
    
    object SeqSearch {
      def printCreatesInClass = macro printCreatesInClass_impl
    
      def printCreatesInClass_impl(c: Context) = {
        import c.universe._
        import scala.collection.generic.SeqFactory
    
        val factorySym = c.typeOf[SeqFactory[Seq]].typeSymbol
    
        def isCreation(tree: Tree) = c.typeCheck(tree) match {
          case Apply(TypeApply(Select(factory, name), _), _) if
            factory.tpe.baseClasses.contains(factorySym) &&
            name == newTermName("apply") => true
          case _ => false
        }
    
        object printCreates extends Traverser {
          override def traverse(tree: Tree) = tree match {
            case application @ Apply(_, args) if isCreation(application) =>
              println("Matched create: " + application)
              super.traverseTrees(args)
            case _ => super.traverse(tree)
          }
        }
    
        printCreates(c.enclosingClass)
    
        c.literalUnit
      }
    }
    

    它是这样工作的:

    scala> class Foo {
         |   SeqSearch.printCreatesInClass
         |   val l = List(1, 2)
         |   val v = Vector(1, 2)
         |   val ab = collection.mutable.ArrayBuffer(1, 2)
         |   val s = Seq(1, 2)
         | }
    Matched create: List(1, 2)
    Matched create: Vector(1, 2)
    Matched create: collection.mutable.ArrayBuffer(1, 2)
    Matched create: Seq(1, 2)
    defined class Foo
    

    查找访问类似——只需将以下方法添加到上面的对象中:

      def printAccessesInClass = macro printAccessesInClass_impl
    
      def printAccessesInClass_impl(c: Context) = {
        import c.universe._
    
        def isAccess(tree: Tree) = c.typeCheck(tree) match {
          case Apply(Select(seq, name), _) if
            seq.tpe <:< typeOf[Seq[Any]] &&
            name == newTermName("apply") => true
          case _ => false
        }
    
        object printAccesses extends Traverser {
          override def traverse(tree: Tree) = tree match {
            case application @ Apply(_, args) if isAccess(application) =>
              println("Matched access: " + application)
              super.traverseTrees(args)
            case _ => super.traverse(tree)
          }
        }
    
        printAccesses(c.enclosingClass)
    
        c.literalUnit
      }
    

    然后:

    scala> class Foo {
         |   SeqSearch.printCreatesInClass
         |   SeqSearch.printAccessesInClass
         |   val xs = List(1, 2, 3)
         |   val xh = xs(0)
         | }
    Matched create: List(1, 2, 3)
    Matched access: xs(0)
    defined class Foo
    

    调整Traverser 的副作用以捕获您感兴趣的任何信息应该很容易,而不仅仅是在编译时打印树。

    【讨论】:

    • 谢谢你,特拉维斯,你的回答真的很有帮助。不过,还有一个问题。你能举一个例子什么时候需要 c.typeCheck(factory).tpe.baseClasses.contains(factorySym)。问是因为我的 factory.tpe.baseClasses.contains(factorySym) 工作正常。
    • 啊,对。没有必要进行两次类型检查——匹配之外的那个就可以了。我已经编辑了答案,谢谢。
    猜你喜欢
    • 2021-10-28
    • 2019-02-24
    • 1970-01-01
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多