Scala:使用常规语言查找长度最多为n的所有字符串

时间:2015-11-26 00:40:24

标签: regex scala regular-language

我有一个(可能是无限的)常规语言,我用正则表达式描述。从这种常规语言我想使用scala获取长度最多为n的所有字符串。一些快速的谷歌搜索告诉我,有一些图书馆可以帮助我。在使用外部库之前,我想知道这是否很容易(就像一个体面的程序员可以在15分钟内实现的东西)在Scala中做自己。如果没有,你可以推荐一些好的库吗?

使我想要的更具体。假设我的语言为A*B*而我的n为3,那么我想要字符串"", "A", "B", "AA", "AB", "BB", "AAA", "AAB", "ABB", "BBB"

2 个答案:

答案 0 :(得分:2)

答案

编辑

  • 26Nov,下午4:30 - 添加基于迭代器的版本以减少运行时和内存消耗。基于Seq的canonic版本位于(1)
  • 的底部
  • 26Nov,2:45 pm - 添加了基于seq的 seq-based 版本,用于canonic,非工作旧版本的canonic位于底部(2)

方法

  • 按规范生成给定字母表中可能的所有单词,长度为n
  • 按正则表达式(在这种情况下为常规语言)过滤生成的单词

代码

object SO {

  import scala.annotation.tailrec
  import scala.collection.{AbstractIterator, Iterator}
  import scala.util.matching.Regex

  def canonic(alphabet: Seq[Char], n: Int): Iterator[String] =
    if (n < 0) Iterator.empty
    else {
      val r: IndexedSeq[Iterator[String]] = for (i <- 1 to n)
        yield new CanonicItr(alphabet, i)
      r.reduce(_ ++ _)
    }

  private class CanonicItr(alphabet: Seq[Char], width: Int) extends AbstractIterator[String] {
    val aSize = alphabet.size
    val alph = alphabet.toVector
    val total = aSizePower(width)

    println("total " + total)

    private var pos = 0L

    private def aSizePower(r: Int): Long = scala.math.pow(aSize, r).toLong

    def stringFor(id: Long): String = {
      val r = for {
        i <- (0 until width).reverse
        // (738 / 10^0) % 10 = 8
        // (738 / 10^1) % 10 = 3
        // (738 / 10^2) % 10 = 7
        charIdx = ((id / (aSizePower(i))) % aSize).toInt
      } yield alph(charIdx)
      r.mkString("")
    }

    override def hasNext: Boolean = pos < total

    override def next(): String = {
      val s = stringFor(pos)
      pos = pos + 1
      s
    }
  }


  def main(args: Array[String]): Unit = {

    // create all possible words with the given alphabet 
    val canonicWordSet = canonic(Seq('a', 'b', 'c'), 8)

    // formal regular language definition
    val languageDef: Regex = "a*b".r

    // determine words of language by filtering the canocic set. 
    val wordsOfLanguage = canonicWordSet.filter(word => languageDef.pattern.matcher(word).matches)

    println(wordsOfLanguage.toList)
  }
}

1)canonic的工作版但具有高内存要求

object SO {

  import scala.util.matching.Regex

  /**
    * Given a sequence of characters (e.g. Seq('a', 'b', 'c') )
    * generates all combinations up to lneght of n (incl.).
    * 
    * @param alphabet sequence of characters
    * @param n is the max length
    * @return all combinations of up to length n. 
    */
  def canonic(alphabet:Seq[Char], n: Int): Seq[String] = {
    def combination( input: Seq[String], chars: Seq[Char]) = {
      for {
        i <- input
        c <- chars
      } yield (i+c)
    }

    @tailrec
    def rec(left: Int, current: Seq[String], accum: Seq[String] ) : Seq[String] = {
      left match {
        case 0 => accum
        case _ => {
          val next = combination( current, alphabet )
          rec( left-1, next, accum ++ next )
        }     
      }
    }

    rec(n, Seq(""), Seq(""))
  }

  def main(args: Array[String]) : Unit = {

    // create all possible words with the given alphabet 
    val canonicWordSet= canonic( Seq('a', 'b', 'c'), 3)

    // formal regular language definition
    val languageDef: Regex = "a*b".r

    // determine words of language by filtering the canocic set. 
    val wordsOfLanguage = canonicWordSet.filter( word => languageDef.pattern.matcher(word).matches )

    println( wordsOfLanguage.toList )
  }
}

2)canonic的非工作版本无法正常工作

def canonic(alphabet:Seq[Char], n: Int): Iterator[String] = {
  for {
    i <- (0 to n).iterator
    combi <- alphabet.combinations(i).map(cs => cs.mkString)
  } yield combi
}

答案 1 :(得分:0)

我还没有完全明白你的意思,这可以吗?

scala> def generator(chars: Seq[Char], n: Int): Iterator[String] = 
     |   (0 to n).iterator flatMap (i => (chars flatMap (_.toString*i) mkString) combinations i)
generator: (chars: Seq[Char], n: Int)Iterator[String]

scala> 

scala> generator("AB", 3) toList
res0: List[String] = List("", A, B, AA, AB, BB, AAA, AAB, ABB, BBB)

scala> generator("ABC", 3) toList
res1: List[String] = List("", A, B, C, AA, AB, AC, BB, BC, CC, AAA, AAB, AAC, ABB, ABC, ACC, BBB, BBC, BCC, CCC)