Well, some considerations need to be made. First, recall that tail does not create objects in lists, and prepending ( :: :) creates only one object for each element added. All in all, as good as you can get, generally speaking.
One way to do this:
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 }
You can get speed with KMP, especially if the pattern search is long.
Now, what is the problem with this algorithm? The problem is that it will not check if the replaced pattern will cause a match before this position. For example, if I replace ACB with C, and I have AACBB input, then the result of this algorithm will be ACB instead of C.
To avoid this problem, you must create a return path. First, you check at what position in your template a replacement may occur:
val positionOfReplacement = pattern.indexOfSlice(replacement)
Then you modify the replacement part of the algorithm:
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 {
This will return to solving the problem.
This algorithm will not work efficiently, however, while multiplying patterns at the same time, and I think this is where you are heading. To do this, you probably need to adapt KMP to do this efficiently, or, otherwise, use DFA to control possible matches. This gets even worse if you want to match AB and ABC.
In practice, the full-hit problem is equivalent to matching regular expression and replacement, where replacement is a matching function. This means that, of course, you can start looking for regular expression algorithms.
EDIT
I forgot to finish my reasoning. If for some reason this technique does not work, then my advice comes with a constant tree vector. Tree vectors allow you to replace partial sequences with a small number of copies.
And if this does not happen, then the solution is a doubly linked list. And choose one of the libraries with a slice replacement - otherwise you may spend too much time debugging a well-known but complicated algorithm.