Это интересная проблема!Мне нужно было реализовать именно это в C # совсем недавно для моей статьи о группировке (поскольку сигнатура функции очень похожа на groupBy
, поэтому ее можно использовать в запросе LINQ как group by
пункт).Реализация C # была довольно уродливой.
В любом случае, должен быть способом выражения этой функции с помощью некоторых простых примитивов.Просто кажется, что библиотека F # не предоставляет никаких функций, которые подходят для этой цели.Мне удалось придумать две функции, которые кажутся в целом полезными и могут быть объединены вместе для решения этой проблемы, поэтому вот они:
// Splits a list into two lists using the specified function
// The list is split between two elements for which 'f' returns 'true'
let splitAt f list =
let rec splitAtAux acc list =
match list with
| x::y::ys when f x y -> List.rev (x::acc), y::ys
| x::xs -> splitAtAux (x::acc) xs
| [] -> (List.rev acc), []
splitAtAux [] list
val splitAt : ('a -> 'a -> bool) -> 'a list -> 'a list * 'a list
Это похоже на то, что мы хотим достичь, ноон разбивает список только на две части (что проще, чем разбивать список несколько раз).Затем нам нужно будет повторить эту операцию, что можно сделать с помощью этой функции:
// Repeatedly uses 'f' to take several elements of the input list and
// aggregate them into value of type 'b until the remaining list
// (second value returned by 'f') is empty
let foldUntilEmpty f list =
let rec foldUntilEmptyAux acc list =
match f list with
| l, [] -> l::acc |> List.rev
| l, rest -> foldUntilEmptyAux (l::acc) rest
foldUntilEmptyAux [] list
val foldUntilEmpty : ('a list -> 'b * 'a list) -> 'a list -> 'b list
Теперь мы можем повторно применять splitAt
(с некоторым предикатом, указанным в качестве первого аргумента) к списку ввода, используяfoldUntilEmpty
, которая дает нам функцию, которую мы хотели:
let splitAtEvery f list = foldUntilEmpty (splitAt f) list
splitAtEvery (<>) [ 1; 1; 1; 2; 2; 3; 3; 3; 3 ];;
val it : int list list = [[1; 1; 1]; [2; 2]; [3; 3; 3; 3]]
Я думаю, что последний шаг действительно хорош :-).Первые две функции довольно просты и могут быть полезны для других целей, хотя они не такие общие, как функции из базовой библиотеки F #.