SSブログ

句構造文法から文を生成する [Scala]

Type 0 以下の句構造規則を与えられるとひたすらその言語に含まれる文を生成していくプログラムを Scala で書いてみました。

キューを用意して幅優先探索の要領で、
- キューには最初はスタートシンボルだけ入っている
- キューから1つとって句構造規則で置き換えたものをまたキューに突っ込む
- キューから取ったものが文の形をしていれば(終端記号だけで構成されていれば)それを結果とする
というのを繰り返していきます。

import scala.collection.mutable._
import java.util.NoSuchElementException

abstract class V
case class NonTerminal(sym: Symbol) extends V { override def toString = sym.name}
case class Terminal(sym: Symbol) extends V { override def toString = sym.name}

type rule = (Seq[V], Seq[V])
type sentential = Seq[V]

case class Grammar(rules: Seq[rule], start: NonTerminal)

case class SentenceIterator(grammar: Grammar) extends Iterator[sentential] {
  val queue = new Queue[sentential]
  queue += List(grammar.start)

  def lookahead() = {
    def isSentence(s: sentential) =
      s.forall { case Terminal(_) => true case _ => false }
    try {
      //println(queue)
      var s = queue.dequeue()
      while (!isSentence(s)) {
        for ((l, r) <- grammar.rules) {
          val i = s.indexOf(l)
          if (i >= 0)
            queue += (s.slice(0, i) ++ r ++ s.slice(i + l.length, s.length))
        }
        //println(queue)
        s = queue.dequeue()
      }
      Some(s)
    } catch {
      case e: NoSuchElementException => None
    }
  }

  var sentence: Option[sentential] = lookahead()
  def hasNext = sentence.isDefined
  def next() = {
    sentence match {
      case Some(s) => sentence = lookahead(); s
      case _ => error("")
    }
  }
}

// DSL for PSG
object Rules { def apply(pairs: rule*) = pairs.toList }
object Start { def apply(nt: Symbol) = NonTerminal(nt) }
def rule(left: Symbol*)(right: Symbol*): rule = {
  def symbolToV(s: Symbol) = 
    if (s.name(0).isUpperCase) NonTerminal(s) else Terminal(s)
  (left.map(symbolToV), right.map(symbolToV))
}

val grammar = Grammar(
  Rules(
    rule ('Name)                    ('tom),
    rule ('Name)                    ('dick),
    rule ('Name)                    ('harry),
    rule ('Sentence)                ('Name),
    rule ('Sentence)                ('List, 'End),
    rule ('List)                    ('Name),
    rule ('List)                    ('List, Symbol(","), 'Name),
    rule (Symbol(","), 'Name, 'End) ('and, 'Name)
  ),
  Start('Sentence)
)

/*
// the a^n b^n c^n grammar
val grammar = Grammar(
  Rules(
    rule ('S)         ('a, 'b, 'c),
    rule ('S)         ('a, 'S, 'Q),
    rule ('b, 'Q, 'c) ('b, 'b, 'c, 'c),
    rule ('c, 'Q)     ('Q, 'c)
  ),
  Start('S)
)
*/

val n = args(0).toInt
for (i <- SentenceIterator(grammar) take n)
  println(i)

文法の例は Parsing Techniques という本から採りました。コメントアウトされているほうは abc, aabbcc, aaabbbccc... を生成する規則なのですがどうも2個目まではすぐ見つかるけど3個目はこの方法だとすごく時間がかかるようです。(そしてこのイテレータの実装だと2個目を取り出すときに3個目も見つけておくようになっているため事実上1個までしか出力できない)


nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。