Хотя мне нравится краткость кода Томаса, я не могу не думать, что более эффективная версия, на которую он намекал, действительно обязательна, особенно если реальная логика сравнения всегда дороже, чем простое интегральное сравнение. Я представляю следующую абстракцию:
let findWindowBeginnings predicate minWindowSize data =
if minWindowSize < 2 then
invalidArg "minWindowSize" "minWindowSize must be greater than 1"
((None, []), data)
||> Seq.fold (fun (window, acc) x ->
if predicate x then
match window with
| Some (start, size) -> let size' = size + 1
let acc' = if size' = minWindowSize
then start::acc
else acc
Some (start, size'), acc'
| _ -> Some (x, 1), acc
else None, acc)
|> snd
|> List.rev
Ваш вариант использования последовательности кортежей дата + температура будет выглядеть так:
let findHeatwaveBeginnings tempThreshold consecutiveDays data =
(consecutiveDays, data)
||> findWindowBeginnings (snd >> (<) tempThreshold)
// alternatively, if you're not a fan of point-free style code:
// findWindowBeginnings (fun (_, maxTemp) -> maxTemp > tempThreshold)
|> List.map fst
Поскольку findWindowBeginnings
управляется Seq.fold
, он, естественно, будет работать естественным образом с массивами и списками. Кроме того, findWindowBeginnings
абсолютно не зависит от исследуемого типа данных, поскольку передаваемый вами предикат выполняет экстраспекцию данных, и предикат, конечно, может работать с любым типом данных, который вам нравится (кортежи, записи, надлежащие классы / структуры и т. Д.). ). Единственное требование состоит в том, чтобы входные данные были логически отсортированы.
F # Ссылка на фрагмент: http://fssnip.net/3u