听完最新的 Stack Overflow 播客后,Peter Norvig 的紧凑型 Python 拼写检查器引起了我的兴趣,所以我决定在 Scala 中实现它,如果我能用函数式 Scala 成语很好地表达它,并且看看它需要多少行代码.
这是整个问题。(我们先不要比较代码行。)
(两个注意事项:如果你愿意,你可以在 Scala 解释器中运行它。如果你需要 big.txt 的副本,或者整个项目,它在 GitHub 上。)
import scala.io.Source
val alphabet = "abcdefghijklmnopqrstuvwxyz"
def train(text:String) = {
"[a-z]+".r.findAllIn(text).foldLeft(Map[String, Int]() withDefaultValue 1)
{(a, b) => a(b) = a(b) + 1}
}
val NWORDS = train(Source.fromFile("big.txt").getLines.mkString.toLowerCase)
def known(words:Set[String]) =
{Set.empty ++ (for(w <- words if NWORDS contains w) yield w)}
def edits1(word:String) = {
Set.empty ++
(for (i <- 0 until word.length) // Deletes
yield (word take i) + (word drop (i + 1))) ++
(for (i <- 0 until word.length - 1) // Transposes
yield (word take i) + word(i + 1) + word(i) + (word drop (i + 2))) ++
(for (i <- 0 until word.length; j <- alphabet) // Replaces
yield (word take i) + j + (word drop (i+1))) ++
(for (i <- 0 until word.length; j <- alphabet) // Inserts
yield (word take i) + j + (word drop i))
}
def known_edits2(word:String) = {Set.empty ++ (for (e1 <- edits1(word);
e2 <- edits1(e1) if NWORDS contains e2) yield e2)}
def correct(word:String) = {
val options = Seq(() => known(Set(word)), () => known(edits1(word)),
() => known_edits2(word), () => Set(word))
val candidates = options.foldLeft(Set[String]())
{(a, b) => if (a.isEmpty) b() else a}
candidates.foldLeft("") {(a, b) => if (NWORDS(a) > NWORDS(b)) a else b}
}
具体来说,我想知道我是否可以使用该correct
功能做任何更清洁的事情。在原始 Python 中,实现更简洁一些:
def correct(word):
candidates = known([word]) or known(edits1(word)) or
known_edits2(word) or [word]
return max(candidates, key=NWORDS.get)
显然,在 Python 中,一个空集将评估为 Boolean False
,因此只有第一个返回非空集的候选者将被评估,从而节省了对edits1
and的潜在昂贵调用known_edits2
。
我想出的唯一解决方案是您在此处看到的版本,其中Seq
调用匿名函数,直到返回一个 non-empty Set
,最后一个保证会这样做。
如此有经验的 Scala-heads,有没有更简洁或更好的方法来做到这一点?提前致谢!