Here is my solution.
First, use a standard sequence-expression with state to accumulate the partial subsequences. Note this portion of the algorithm is, I believe, just as clea in either imperative or functional programming. The key thing is to abstract and encapsulate the imperative programming into a lovely reusable functional programming sequence processor. The F# "Seq" library is full of lots of examples like this.
/// Return all the subsequences starting with a given character. Also return any initial
/// sequence not starting with that character. Do this by keeping an internal accumulator and publishing from it
let SequencesStartingWith n (s:seq<_>) =
seq { use ie = s.GetEnumerator()
let acc = new ResizeArray<_>()
while ie.MoveNext() do
let x = ie.Current
if x = n && acc.Count > 0 then
yield ResizeArray.to_list acc
acc.Clear()
acc.Add x
if acc.Count > 0 then
yield ResizeArray.to_list acc }
Next, given an initial element "n" and a sequence "s", the analysis is straight forward. Note the use of Seq.distinct to ensure no repeats.
let analyze n s =
let subsequences = SequencesStartingWith n s |> Seq.distinct |> Seq.to_list
let subsequenceToName = subsequences |> List.mapi (fun i x -> x,char (int 'A' + i)) |> Map.of_list
let result = subsequences |> List.map (fun x -> subsequenceToName.[x])
result
This is OK, but loses information, e.g. what do A and B mean in the result??? This leads is a perfect example of F# object oriented programming to return multiple interesting results from an analysis and give those results good names:
/// Do the subsequence analysis on an input sequence and return an object revealing the
/// forward mapping table 'A' to [1;3;4], the backward mapping table '[1;3;4]' to 'A',
/// the overall result, and the actual unique subsequences found.
type SequenceMarkup<'a>(marker,s:seq<'a>) =
let subsequences = SequencesStartingWith marker s |> Seq.distinct |> Seq.to_list
let mapping = subsequences |> List.mapi (fun i x -> x,char (int 'A' + i))
let subsequenceToName = mapping |> Map.of_list
let result = subsequences |> List.map (fun x -> subsequenceToName.[x])
let nameToSubsequence = mapping |> List.map (fun (x,y) -> (y,x)) |> Map.of_list
/// Get the name corresponding to a particular subsequence
member x.NameForSubsequence(subsequence) = subsequenceToName.TryFind(subsequence)
/// Get the subsequence corresponding to a particular name
member x.SubsequenceForName(name) = nameToSubsequence.TryFind(name)
/// Get the subsequence corresponding to a particular name
member x.Subsequences = subsequences
/// Get the input list with subsequences replaced by names
member x.Result = result
Now run
let markup = SequenceMarkup(1,[1; 6; 7; 8; 1; 8; 7; 4; 3; 2; 1; 6; 7; 8; 1; 6; 5; 1; 9; 1; 6; 5; 1; 6; 7; 8; 1; 9; 1; 9; 1; 6; 7; 8; 1; 3; ])
markup.Result
markup.Subsequences
markup.<FONT size=2>SubsequenceForName 'A'</FONT>