Конечный автомат - подходящее решение здесь. Существует два распространенных способа реализации одного:
Аппаратно реализован в виде набора взаимно-рекурсивных функций, где выполняемая функция представляет текущее состояние. Это может быть очень эффективным, но требует устранения хвостовых вызовов, батутов или goto.
Эмулируется в виде структуры данных, представляющей текущее состояние и функцию этого состояния, и нового входного значения, которое обновляет состояние.
Этот материал - хлеб с маслом функционального программирования. Вот элегантное решение вашей проблемы, написанное в прежнем стиле на F # и работающее на вашем собственном наборе данных:
> let rec skip = function
| _, yss, [] -> yss
| [_; _; _] as ys, yss, xs -> record ([], ys, yss, xs)
| ys, yss, x::xs when x >= 5 -> skip (x::ys, yss, xs)
| ys, yss, x::xs -> skip ([], yss, xs)
and record = function
| ys', ys, yss, [] -> (ys' @ ys) :: yss
| [_; _], ys, yss, xs -> skip ([], ys :: yss, xs)
| ys', ys, yss, x::xs when x < 3 -> record (x::ys', ys, yss, xs)
| ys', ys, yss, x::xs -> record ([], x::ys' @ ys, yss, xs);;
val skip : int list * int list list * int list -> int list list
val record : int list * int list * int list list * int list -> int list list
> let run xs = skip([], [], xs) |> List.map List.rev |> List.rev;;
val run : int list -> int list list
> run [0;0;0;7;8;0;0;2;5;6;10;11;10;13;5;0;1;0];;
val it : int list list = [[5; 6; 10; 11; 10; 13; 5]]
и вот то же решение, написанное в последнем стиле:
> type 'a state =
| Skip of 'a list
| Record of 'a list * 'a list;;
type 'a state =
| Skip of 'a list
| Record of 'a list * 'a list
> let rec apply (state, yss) x =
match state, yss with
| Skip([_; _; _] as ys), yss -> apply (Record([], ys), yss) x
| Skip ys, yss when x >= 5 -> Skip(x::ys), yss
| Skip ys, yss -> Skip[], yss
| Record([_; _], ys), yss -> apply (Skip[], ys :: yss) x
| Record(ys', ys), yss when x < 3 -> Record (x::ys', ys), yss
| Record(ys', ys), yss -> Record ([], x::ys' @ ys), yss;;
val apply : int state * int list list -> int -> int state * int list list
> let run xs =
match List.fold apply (Skip [], []) xs with
| Skip _, yss -> yss
| Record(ys', ys), yss -> (ys' @ ys) :: yss
|> List.map List.rev |> List.rev;;
val run : int list -> int list list
> run [0;0;0;7;8;0;0;2;5;6;10;11;10;13;5;0;1;0];;
val it : int list list = [[5; 6; 10; 11; 10; 13; 5]]
Обратите внимание, как первое решение съедает весь ввод за один раз, тогда как последнее откусывает один элемент данных за раз и возвращает новое состояние, готовое к употреблению другого элемента данных. Следовательно, последний применяется к каждому датуму по очереди, используя fold
, и окончательное недоеденное состояние должно быть соответствующим образом израсходовано перед возвратом.