F# 在fitler中返回条件?
我有下面的代码来过滤F# 在fitler中返回条件?,f#,F#,我有下面的代码来过滤Seq,如果没有返回,则返回错误 let s = nodes |> Seq.filter(fun (a, _, _, _) -> if a.ToLower().Contains(key1)) // condition 1 then true else false // Error m
Seq
,如果没有返回,则返回错误
let s = nodes
|> Seq.filter(fun (a, _, _, _) -> if a.ToLower().Contains(key1)) // condition 1
then true
else false // Error message should have Key1
|> Seq.filter(....) // condition 2
|> Seq.filter(....) // condition 3
.....
|> Seq.filter(function // condition N
| _, Some date, _, _ -> date >= StartPeriod
| _ -> false // put StartPeriod in the final message s is not empty before this step
)
if Seq.isEmpty s
then sprintf "Failed by condition 1 (%s) or condition 2 (%s) .... or condition N (Date > %s)"
key1, ...., (StartPeriod.ToShortDateSTring())
else ....
最后的错误消息sprintf
将包含所有筛选条件。这是一种让代码只返回那些(或只返回最后一个)使s
为空的方法吗
根据rmunn的回答,我修改了它,以返回所有导致清空列表的过滤器
let rec filterSeq filterList input msgs =
match filterList with
| [] -> input, msgs
| (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, (List.append msgs [label])
else
filterSeq filters result (List.append msgs [label])
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 intFiltersWithLabels <| List.empty
让rec filterSeq filterList输入MSG=
将过滤器列表与
|[]->输入,msgs
|(标签,过滤器)::过滤器->
让结果=输入|>顺序过滤器
如果结果|>序号为空,则
printfn“过滤器\%s\”清空了输入“标签
Seq.empty,(List.append-msgs[标签])
其他的
filterSeq筛选结果(List.append msgs[标签])
让intFiltersWithLabels=[
“奇数”,乐趣x->x%2 0
“不能被3整除”,乐趣x->x%3 0
“不能被5整除”,乐趣x->x%5 0
“偶数”,乐趣x->x%2=0
“无法到达这里”,乐趣x->x%7 0
]
{1..20}|>filterSeq intFiltersWithLabels我要做的是创建一个过滤器列表,以及一个递归函数,一次应用一个过滤器。如果刚刚应用的筛选器返回空序列,则停止,打印刚刚清空输入的筛选器,然后返回空序列。否则,继续循环递归函数,依次使用列表中的下一个过滤器,直到最后没有输入,或者运行了整个过滤器列表,并且在通过所有过滤器后仍有一些输入
这里有一些示例代码来说明我的意思。请注意,我是如何将标签放在每个过滤器函数的前面的,这样我就不会看到像“过滤器清空了输入”这样的输出,而是看到每个过滤器都有一个合理的可读标签
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
关于这个版本的函数,有几点需要注意:听起来您想要的是,如果过滤器一直运行,而没有清空输入,那么您就不希望返回任何过滤器标签。如果我误解了您的意思,请将|[]->input、[]
行替换为|[]->input、labelsofar
,以获取输出中的所有标签。第二件需要注意的事情是,我改变了这个函数的“形状”:它不返回seq,而是返回一个2元组(result seq,过滤器标签列表)。如果结果序列不为空,则筛选器标签列表将为空,但如果结果序列最终为空,则筛选器标签列表将包含应用的所有筛选器,而不仅仅是减小输入大小的所有筛选器
如果您真正需要的是检查输入的大小是否减小,并且只打印过滤掉某些内容的过滤器的标签,那么请查看Funk的答案以了解如何检查,但请注意,Seq.length
必须在整个原始序列中运行,并每次应用所有过滤器。所以这是一个缓慢的操作。如果您的输入数据集很大,那么最好使用Seq.empty
逻辑。使用它并决定什么最适合您的需要。您可以使用decorator将日志记录/错误处理代码与业务逻辑分开
首先,我们的记录器
open System.Text
type Logger() =
let sb = StringBuilder()
member __.log msg =
sprintf "Element doesn't contain %s ; " msg |> sb.Append |> ignore
member __.getMessage() =
sb.ToString()
现在,我们要包装Seq.filter
,这样每次我们过滤掉一些元素时它都会记录下来
以一个例子结束
let logger = Logger()
let filterLog msg f seq = filterBuilder logger msg f seq
let seq = ["foo" ; "bar"]
let r =
seq
|> filterLog "f"
(fun s -> s.Contains("f"))
|> filterLog "o"
(fun s -> s.Contains("o"))
|> filterLog "b"
(fun s -> s.Contains("b"))
|> filterLog "a"
(fun s -> s.Contains("a"))
logger.getMessage()
val it:string=“元素不包含f;元素不包含b;”
“bar”
立即被过滤掉,生成第一条消息<代码>“foo”
第三次出现。还要注意,行中的第二个和最后一个管道不要记录任何消息。问题的最后一句没有意义。我把它通读了好几遍,但无法理解它的意思。你能试着重新表述一下这个问题吗?过滤器可能会逐渐删除一些元素。当s为空时,我希望错误消息显示一个筛选器(或一个筛选器之前的所有文件)使s从非空变为空。有趣的是,您的解决方案看起来确实更实用:),尽管使用其他高阶函数进行编写似乎很难。如果您可能需要其他类型的高阶函数(假设您需要Seq.filter
和Seq.map
)然后更改列表中的内容。不要让列表包含过滤器,而是让它包含“步骤”,其中步骤是Seq
函数:Seq.filter(fun x->x%20)
将是一个步骤,而Seq.map(fun x->x+1)
也将是一个步骤。然后,您将执行let result=input |>Seq.filter filter
,而不是let result=input |>step
。当然,但这会增加不必要的复杂性,并失去灵活性。例如,您将在Seq.map之后检查Seq.isEmpty。或者,添加更多的复杂性,将其挑选出来。。。@Funk-您的解决方案也是一个很好的解决方案:一个简单的日志包装器是一个非常有用的工具,可以保存在您的工具箱中。顺便说一句,将一个函数包装在另一个函数周围也是非常有用的,所以不要觉得您的答案“功能不如我的”。使用List.append labelsofar[label]
是O(N)因为F#list是单链表,你需要做N次,所以总共是O(N^2)。添加到F#list的头上是O(1),你做了N次,所以总共是O(N)。然后,因为你一直在添加到头上,所以列表是最新的第一个,你希望最不最近的第一个,所以你要做最后的list.rev
(一个O(N)操作)一次。所以我这样做的方式是O(N),而你所问的List.append
方法是O(N^2);这就是区别。我喜欢围绕工作函数的包装方法。我不喜欢这里的一件事是重复使用Seq.length
,这是做两个额外的O(N)操作
let filterBuilder (logger:Logger) msg f seq =
let res = Seq.filter f seq
if Seq.length seq > Seq.length res then logger.log msg
res
let logger = Logger()
let filterLog msg f seq = filterBuilder logger msg f seq
let seq = ["foo" ; "bar"]
let r =
seq
|> filterLog "f"
(fun s -> s.Contains("f"))
|> filterLog "o"
(fun s -> s.Contains("o"))
|> filterLog "b"
(fun s -> s.Contains("b"))
|> filterLog "a"
(fun s -> s.Contains("a"))
logger.getMessage()