我需要这是一个高效的解析器,所以@dhg 的答案对我不起作用。这是我确定的答案。它使用不区分大小写的Trie 的变体,但在哪里
- 最短的比赛获胜
- 我们不使用整个搜索输入。我们最多只消耗一场比赛,不会更多。
这是特里:
case class CaseInsensitiveTrie[T](children: List[Node[T]] = Nil) {
def add(key: List[Char], value: T): CaseInsensitiveTrie[T] = {
def loop(key: List[Char], nodes: List[Node[T]]): List[Node[T]] = key match {
case c::Nil =>
nodes.find(_.key == c) match {
case Some(node) => nodes // No updating values
case None => Node(c, Some(value), Nil) :: nodes
}
case c::tail =>
nodes.find(_.key == c) match {
case Some(node) =>
val children = loop(tail, node.children)
val newNode = Node(c, None, children)
newNode :: nodes.filterNot(_.key == c)
case None =>
val children = loop(tail, Nil)
val newNode = Node(c, None, children)
newNode :: nodes
}
case Nil => ??? // Programmer error
}
CaseInsensitiveTrie(loop(key, children))
}
def find(input: List[Char]): Option[(T, List[Char])] = {
def loop(input: List[Char], nodes: List[Node[T]]): Option[(T, List[Char])] = input match {
case c::Nil =>
nodes.find(_.key.toUpper == c.toUpper).flatMap(_.value.map(_ -> Nil))
case c::tail =>
nodes.find(_.key.toUpper == c.toUpper).flatMap {
case node if node.value.isDefined =>
node.value.map(_ -> tail)
case node =>
loop(tail, node.children)
}
}
loop(input, children)
}
}
object CaseInsensitiveTrie {
case class Node[T](key: Char, value: Option[T], children: List[CaseInsensitiveTrie.Node[T]])
}
只有 2 个函数 - find 和 add。只用不区分大小写的关键字填充 trie,然后我们创建这个 unapply 函数:
val trie = CaseInsensitiveTrie[Token]()
.add("SELECT".toList, SELECT)
.add("FROM".toList, FROM)
.add("WHERE".toList, WHERE)
.add("AS".toList, AS)
object Keyword {
def unapply(input: List[Char]): Option[(Token, List[Char])] = {
trie.find(input)
}
}
所以在匹配中我们可以测试匹配并同时拉取匹配和输入的剩余部分:
input match {
case '*'::tail => (STAR, Scan(tail, location + 1).trim)
case '('::tail => (LPAREN, Scan(tail, location + 1).trim)
case ')'::tail => (RPAREN, Scan(tail, location + 1).trim)
case '.'::tail => (DOT, Scan(tail, location + 1).trim)
case ','::tail => (COMMA, Scan(tail, location + 1).trim)
case '='::tail => (EQ, Scan(tail, location + 1).trim)
case '<'::'>'::tail => (NE, Scan(tail, location + 2).trim)
case '>'::tail => (GT, Scan(tail, location + 1).trim)
case '>'::'='::tail => (GE, Scan(tail, location + 2).trim)
case '<'::tail => (LT, Scan(tail, location + 1).trim)
case '>'::'='::tail => (LE, Scan(tail, location + 2).trim)
case Keyword(kw, tail) if tail.headOption.fold(true)(_.isWhitespace) =>
(kw, Scan(tail, location + kwLength(kw)).trim)
}
这不是可用的最快的 Trie 实现,但它可以满足我的需求。如果性能完全成为问题,我可以将其压缩为尝试