好的,需要考虑一些问题。首先,回想一下,在列表中,tail
不会创建对象,而前置 ( ::
) 只会为每个前置元素创建一个对象。一般来说,这几乎和你能得到的一样好。
这样做的一种方法是:
def myReplace(input: List[Op], pattern: List[Op], replacement: List[Op]) = {
// This function should be part of an KMP algorithm instead, for performance
def compare(pattern: List[Op], list: List[Op]): Boolean = (pattern, list) match {
case (x :: xs, y :: ys) if x == y => compare(xs, ys)
case (Nil, Nil) => true
case _ => false
}
var processed: List[Op] = Nil
var unprocessed: List[Op] = input
val patternLength = pattern.length
val reversedReplacement = replacement.reverse
// Do this until we finish processing the whole sequence
while (unprocessed.nonEmpty) {
// This inside algorithm would be better if replaced by KMP
// Quickly process non-matching sequences
while (unprocessed.nonEmpty && unprocessed.head != pattern.head) {
processed ::= unprocessed.head
unprocessed = unprocessed.tail
}
if (unprocessed.nonEmpty) {
if (compare(pattern, unprocessed)) {
processed :::= reversedReplacement
unprocessed = unprocessed drop patternLength
} else {
processed ::= unprocessed.head
unprocessed = unprocessed.tail
}
}
}
processed.reverse
}
您可以通过使用 KMP 来提高速度,尤其是在搜索的模式很长的情况下。
现在,这个算法有什么问题?问题是它不会测试替换的模式是否在该位置之前导致匹配。例如,如果我用 C 替换 ACB,并且我有一个输入 AACBB,那么这个算法的结果将是 ACB 而不是 C。
为避免此问题,您应该创建回溯。首先,您检查替换可能发生在模式中的哪个位置:
val positionOfReplacement = pattern.indexOfSlice(replacement)
然后,您修改算法的替换部分:
if (compare(pattern, unprocessed)) {
if (positionOfReplacement > 0) {
unprocessed :::= replacement
unprocessed :::= processed take positionOfReplacement
processed = processed drop positionOfReplacement
} else {
processed :::= reversedReplacement
unprocessed = unprocessed drop patternLength
}
} else {
这将足以解决问题。
但是,该算法不能有效地同时处理乘法模式,我猜这就是您要去的地方。为此,您可能需要对 KMP 进行一些调整,以有效地执行此操作,或者,使用 DFA 来控制可能的匹配。如果您想同时匹配 AB 和 ABC,情况会变得更糟。
在实践中,全面的问题等同于正则表达式匹配和替换,其中替换是匹配的函数。这意味着,当然,您可能想开始研究正则表达式算法。
编辑
我忘了完成我的推理。如果该技术由于某种原因不起作用,那么我的建议是使用不可变的基于树的向量。基于树的向量能够以少量复制替换部分序列。
如果不这样做,那么解决方案就是双向链表。并从带有切片替换的库中选择一个 - 否则您最终可能会花费太多时间来调试已知但棘手的算法。