Какой самый элегантный способ найти пары слов в тексте с помощью Scala? - PullRequest
1 голос
/ 15 июля 2011

Дан список пар слов

val terms = ("word1a", "word1b") :: ("word2a", "word2b") :: ... :: Nil

Какой самый элегантный способ проверить в Scala, если хотя бы одна пар встречается в тексте? Тест должен завершиться как можно быстрее, когда он попадет в первый матч. Как бы вы решили это?

РЕДАКТИРОВАТЬ: Чтобы быть более точным, я хочу знать, если оба слова пары появляются где-то (не обязательно в порядке) в тексте. Если это относится к одной из пар в списке, метод должен вернуть true. Нет необходимости возвращать подобранную пару, равно как и важно, если найдено более одной пары.

Ответы [ 3 ]

6 голосов
/ 15 июля 2011
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 намного более эффективным. Вы не хотели бы использовать что-то последовательное, как список для этого.

РЕДАКТИРОВАТЬ 2: Обновлено для уточнения в требовании!

РЕДАКТИРОВАТЬ 3: изменено contains на apply согласно предложению в комментарии

1 голос
/ 15 июля 2011

РЕДАКТИРОВАТЬ - кажется, двусмысленная формулировка вашего вопроса означает, что я ответил на другой вопрос :

Поскольку вы, по сути, спрашиваете любую из пары;с тем же успехом можно объединить все это в один большой набор.

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
0 голосов
/ 15 июля 2011

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

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

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)))

Вы могли бы еще улучшить вещи, поместив все термины в набор, и даже не потрудившись проверить WordPirlist, если только слово из текста не было в этом наборе.

Если вы имеете в виду, что слова должны встречаться по порядку рядом друг с другом, вам следует изменить 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
}
...