Построение вариаций вложенных case-классов

Итак, я получил что-то вроде этого:

abstract class Term
case class App(f:Term,x:Term) extends Term
case class Var(s:String) extends Term
case class Amb(a:Term, b:Term) extends Term //ambiguity

И Term может выглядеть так:

App(Var(f),Amb(Var(x),Amb(Var(y),Var(z))))

Так что мне нужны все варианты, указанные классом Amb. Это используется для представления неоднозначного леса синтаксического анализа, и я хочу проверить каждый возможный вариант и выбрать правильный. В этом примере мне понадобится:

App(Var(f),Var(x))
App(Var(f),Var(y))
App(Var(f),Var(z))

Как лучше всего создать эти варианты в scala? Эффективность была бы хорошей, но на самом деле это не требование. Если возможно, я предпочитаю воздерживаться от использования отражения.


person schlicht    schedule 26.09.2013    source источник
comment
Обратите внимание, что вы можете захотеть представить тот факт, что некоторые выражения являются детерминированными, а некоторые не находятся в системе типов (чтобы, например, вам не нужно было беспокоиться о сопоставлении Amb после выполнения этой операции). Вот быстрый набросок того, как это может выглядеть.   -  person Travis Brown    schedule 26.09.2013
comment
Да, я думал об этом, но у меня уже есть довольно большой абстрактный синтаксис, и это сделало бы его огромным.   -  person schlicht    schedule 03.10.2013


Ответы (3)


Scala обеспечивает сопоставление с образцом для решения подобных проблем. Решение будет выглядеть так:

def matcher(term: Term): List[Term] = {
  term match {
    case Amb(a, b) => matcher(a) ++ matcher(b)
    case App(a, b) => for { va <- matcher(a); vb <- matcher(b) } yield App(va, vb)
    case v: Var    => List(v)
  }
}
person Mark    schedule 26.09.2013

Вы можете сделать это довольно аккуратно с помощью рекурсивной функции, которая проходит по дереву и расширяет неоднозначности:

sealed trait Term
case class App(f: Term, x: Term) extends Term
case class Var(s: String) extends Term
case class Amb(a: Term, b: Term) extends Term

def det(term: Term): Stream[Term] = term match {
  case v: Var    => Stream(v)
  case App(f, x) => det(f).flatMap(detf => det(x).map(App(detf, _)))
  case Amb(a, b) => det(a) ++ det(b)
}

Обратите внимание, что я использую типаж seal вместо абстрактного класса, чтобы воспользоваться возможностью компилятора проверять полноту.

Он работает так, как ожидалось:

scala> val app = App(Var("f"), Amb(Var("x"), Amb(Var("y"), Var("z"))))
app: App = App(Var(f),Amb(Var(x),Amb(Var(y),Var(z))))

scala> det(app) foreach println
App(Var(f),Var(x))
App(Var(f),Var(y))
App(Var(f),Var(z))

Если вы можете изменить Term API, вы могли бы более или менее эквивалентно добавить туда метод def det: Stream[Term].

person Travis Brown    schedule 26.09.2013

Поскольку мой абстрактный синтаксис довольно большой (а у меня их несколько), я попытал счастья с Киамой. Итак, вот версия, которую Трэвис Браун и Марк опубликовали вместе с Киамой.

Это не красиво, но я надеюсь, что это работает. Комментарии приветствуются.

  def disambiguateRule: Strategy = rule {
    case Amb(a: Term, b: Term) =>
      rewrite(disambiguateRule)(a).asInstanceOf[List[_]] ++
      rewrite(disambiguateRule)(b).asInstanceOf[List[_]]
    case x => 
      val ch = getChildren(x)
      if(ch.isEmpty) {
        List(x)
      }
      else {
        val chdis = ch.map({ rewrite(disambiguateRule)(_) }) // get all disambiguate children
        //create all combinations of the disambiguated children
        val p = combinations(chdis.asInstanceOf[List[List[AnyRef]]]) 
        //use dup from Kiama to recreate the term with every combination
        val xs = for { newchildren <- p } yield dup(x.asInstanceOf[Product], newchildren.toArray)
        xs
      }
  }

  def combinations(ll: List[List[AnyRef]]): List[List[AnyRef]] = ll match {
    case Nil      => Nil
    case x :: Nil => x.map { List(_) }
    case x :: xs  => combinations(xs).flatMap({ ys => x.map({ xx => xx :: ys }) })
  }

  def getChildren(x: Any): List[Any] = {
    val l = new ListBuffer[Any]()
    all(queryf {
      case a => l += a
    })(x)
    l.toList
  }
person schlicht    schedule 04.10.2013