[英]What's the most elegant way to find word pairs in a text with Scala?
给定一个单词对列表
val terms = ("word1a", "word1b") :: ("word2a", "word2b") :: ... :: Nil
Scala 中测试文本中是否至少有一对出现的最优雅的方法是什么? 测试应在遇到第一场比赛时尽快终止。 你会怎么解决?
编辑:更准确地说,我想知道一对单词是否出现在文本中的某个地方(不一定按顺序)。 如果列表中的一对是这种情况,则该方法应返回true
。 没有必要返回匹配的对,如果超过一对匹配也不重要。
scala> val text = Set("blah1", "word2b", "blah2", "word2a")
text: scala.collection.immutable.Set[java.lang.String] = Set(blah1, word2b, blah2)
scala> terms.exists{case (a,b) => text(a) && text(b)}
res12: Boolean = true
编辑:请注意,使用集合来表示文本中的标记会使contains
的查找更加有效。 您不想为此使用诸如 List 之类的顺序。
编辑2:更新以澄清要求!
编辑 3:更改contains
以根据评论中的建议apply
编辑 - 似乎您问题的含糊措辞意味着我回答了一个不同的问题:
因为您本质上是在要求这对中的任何一个; 您不妨将所有这些扁平化为一个大集合。
val words = (Set.empty[String] /: terms) { case (s, (w1, w2)) => s + w1 + w2 }
然后您只是询问文本中是否存在这些中的任何一个:
text.split("\\s") exists words
这很快,因为我们可以使用Set
的结构来快速查找单词是否包含在文本中; 由于“存在”,它提前终止:
scala> val text = "blah1 blah2 word2b"
text: java.lang.String = blah1 blah2 word2b
如果您的文本很长,您可能希望Stream
它,以便懒惰地计算下一个要测试的单词,而不是预先将字符串拆分为子字符串:
scala> val Word = """\s*(.*)""".r
Word: scala.util.matching.Regex = \s*(.*)
scala> def strmWds(text : String) : Stream[String] = text match {
| case Word(nxt) => val (word, rest) = nxt span (_ != ' '); word #:: strmWds(rest)
| case _ => Stream.empty
| }
strmWds: (text: String)Stream[String]
现在你可以:
scala> strmWds(text) exists words
res4: Boolean = true
scala> text.split("\\s") exists words
res3: Boolean = true
我假设这对元素的两个元素都必须出现在文本中,但在哪里都没有关系,哪对出现也没有关系。
我不确定这是最优雅的,但它还不错,如果您期望文本可能包含单词(因此您不需要阅读所有内容)并且您可以生成一个迭代器,一次给你一个单词:
case class WordPair(one: String, two: String) {
private[this] var found_one, found_two = false
def check(s: String): Boolean = {
if (s==one) found_one = true
if (s==two) found_two == true
found_one && found_two
}
def reset {
found_one = false
found_two = false
}
}
val wordpairlist = terms.map { case (w1,w2) => WordPair(w1,w2) }
// May need to wordpairlist.foreach(_.reset) first, if you do this on multiple texts
text.iterator.exists(w => wordpairlist.exists(_.check(w)))
您可以通过将所有术语放在一个集合中来进一步改进事情,甚至无需检查单词对列表,除非文本中的单词在该集合中。
如果您的意思是单词必须按顺序相邻出现,则应将check
更改为
def check(s: String) = {
if (found_one && s==two) found_two = true
else if (s==one) { found_one = true; found_two = false }
else found_two = false
found_one && found_two
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.