Что я хотел бы сделать, это составить список фильтров и рекурсивную функцию, которая применяет их по одному.Если фильтр, который только что был применен, возвращает пустую последовательность, остановитесь, напечатайте фильтр, который только что опустел ваш ввод, и верните эту пустую последовательность.В противном случае продолжайте циклически повторять рекурсивную функцию, применяя фильтр next в списке по очереди, пока либо вы не окажетесь без ввода, либо не пройдете весь список фильтров, и после прохождения все еще останется некоторое количество ввода.фильтры.
Вот пример кода, чтобы проиллюстрировать, что я имею в виду.Обратите внимание, как я поместил метки перед каждой функцией фильтра, так что я не вижу вывод, такой как «Фильтр <fun:filtersWithLabels@4>
опустошил вход», но вместо этого я вижу понятную для человека метку для каждого фильтра.
let rec filterSeq filterList input =
match filterList with
| [] -> input
| (label, filter) :: filters ->
let result = input |> Seq.filter filter
if result |> Seq.isEmpty then
printfn "The \"%s\" filter emptied out the input" label
Seq.empty
else
filterSeq filters result
let intFiltersWithLabels = [
"Odd numbers", fun x -> x % 2 <> 0
"Not divisible by 3", fun x -> x % 3 <> 0
"Not divisible by 5", fun x -> x % 5 <> 0
"Even numbers", fun x -> x % 2 = 0
"Won't reach here", fun x -> x % 7 <> 0
]
{ 1..20 } |> filterSeq filtersWithLabels
// Prints: The "Even numbers" filter emptied out the input
Если вы хотите напечатать все фильтры до тех пор, пока тот не очистит входные данные, то вы просто переместите этот вызов printfn на одну строку вне выражения if
.Тот факт, что рекурсия останавливается после того, как ввод пуст, означает, что вы не увидите никаких вызовов printfn после фильтра, который опустошил ввод.
Обратите внимание, что способ, которым я написал функцию, предполагает, что ваш исходный ввод будетне быть пустым.Если ваш исходный ввод был пустым, то функция зачислит первый фильтр на очистку ввода и напечатает метку первого фильтра.Вы можете решить это достаточно легко, проверив пустой ввод перед проверкой пустого результата, но я не стал беспокоиться, поскольку это всего лишь демонстрационный код.Просто знайте об этом, если ваш реальный ввод может быть пустым в вашем фактическом сценарии использования.
Обновление: Если вам нужно вернуть список этикеток, а не просто напечатать их, тогда сделайтечто второй параметр, который вы передаете через функцию filterSeq
.Примерно так:
let matchingFilters filterList input =
let rec filterSeq filterList labelsSoFar input =
match filterList with
| [] -> input, [] // Note NO labels returned in this case!
| (label, filter) :: filters ->
let result = input |> Seq.filter filter
if result |> Seq.isEmpty then
Seq.empty, (label :: labelsSoFar)
else
filterSeq filters (label :: labelsSoFar) result
let result, labels = filterSeq filterList [] input
result, List.rev labels
let filtersWithLabels = [
"Odd numbers", fun x -> x % 2 <> 0
"Not divisible by 3", fun x -> x % 3 <> 0
"Not divisible by 5", fun x -> x % 5 <> 0
"Even numbers", fun x -> x % 2 = 0
"Won't reach here", fun x -> x % 7 <> 0
]
{ 1..20 } |> matchingFilters filtersWithLabels
// Returns: ["Odd numbers"; "Not divisible by 3"; "Not divisible by 5"; "Even numbers"]
Несколько замечаний по поводу этой версии функции: звучит так, как будто вы хотите, чтобы, если фильтры работали до конца, не опустошая ввод, вы хотитеНИКАКИЕ метки фильтра не возвращаются.Если я вас неправильно понял, замените строку | [] -> input, []
на | [] -> input, labelsSoFar
, чтобы получить все метки в выводе.Второе, на что следует обратить внимание, это то, что я изменил «форму» этой функции: вместо возврата seq он возвращает 2-х кортеж (результат seq, список меток фильтра).Список меток фильтров будет пустым, если результат seq не пустой, но если результат seq окажется пустым, то список меток фильтров будет содержать всех примененных фильтров , а не только всефильтры, уменьшающие размер входных данных.
Если вам действительно нужно проверить, не уменьшен ли размер входных данных, и вывести только метки фильтров, отфильтровавших что-то, тогдапосмотрите на ответ Фанка о том, как это проверить, но помните, что Seq.length
должен пройти через всю исходную последовательность и применить все фильтры до этой точки, каждый раз .Так что это медленная операция.Если ваш входной набор данных большой, то лучше придерживаться логики Seq.empty
.Поиграйте с ним и решите, что лучше всего соответствует вашим потребностям.