我刚刚进行了CodeWars练习-“创建一个函数,该函数接受非负整数和字符串的列表,并返回过滤出字符串的新列表”。
我的解决方案使用List.filter,但在其中一种极端情况下失败了。因此,我查看了他们的解决方案,并使用了List.choose-它似乎与我的版本几乎相同,只是在决定是否将结果包含在新列表中之前将结果转换为选项。
我很困惑-有人可以解释什么时候最好使用“选择”,什么时候最好使用“过滤器”?
答案 0 :(得分:6)
我认为您已经观察到了答案的实质:filter
允许您测试条件,但是使用choose
也可以在相同的表达式中投影您的值,这需要一个如果使用map
,请单独使用filter
。
由于问题陈述不清楚(列表不能同时包含整数和字符串,除非将它们装箱;即列表的类型为obj list
),我们可以同时查看场景。使用map
时请注意其他filter
功能。
// List of strings that may contain integer representations
["1"; "abc"; "2"; "def"]
|> List.choose (System.Int32.TryParse >> function
| true, i -> Some i
| _ -> None )
["1"; "abc"; "2"; "def"]
|> List.map System.Int32.TryParse
|> List.filter fst
|> List.map snd
两个表达式都返回int list = [1; 2]
。
// List of object that are either of type int or of type string
[box 1; box "abc"; box 2; box "def"]
|> List.choose (function
| :? int as i -> Some i
| _ -> None )
[box 1; box "abc"; box 2; box "def"]
|> List.filter (fun i -> i :? int)
|> List.map unbox<int>
在使用obj list
作为输入的情况下,投影用于提供正确的结果类型。这可以以不同的方式完成,例如带有注释的let绑定。
最后,两者之间的决定取决于您的个人喜好。
答案 1 :(得分:3)
List.choose
严格比List.filter
更通用。您只能使用List.filter
来实现List.choose
,而不能使用其他方式。仅当您不能使用List.choose
时,才应使用List.filter
代替List.choose : ('T -> 'U option) -> 'T list -> 'U list
List.filter : ('T -> bool) -> 'T list -> 'T list
,因为它更简单并且可以更准确地描述您的意图。
您可以单独从类型签名中观察到这种差异。
List.filter
List.choose
可以通过let filter : ('T -> bool) -> 'T list -> 'T list =
fun predicate ->
List.choose (fun x -> if predicate x then Some x else None)
来实现,如下所示:
List.choose
但是,可以使用List.filter
以及 List.map
和Option.get' (it is in fact called
filterMap`(在许多语言和库中)实现 let choose : ('T -> 'U option) -> 'T list -> 'U list =
fun f list ->
list
|> List.map f
|> List.filter (fun x -> x <> None)
|> List.map Option.get
(效率低下):
Option.get
请注意,None
可能会引发异常,但这里不会出现异常,因为我们已经过滤掉了会导致此异常的List.choose
。但是,由于它不安全,很容易出错,并且由于此实现方式效率不高,所以开箱即用with open(file, 'r') as file:
lines = [line.replace('Test.xml', 'Test_n.xml')
for line in file
if 'Test.xml' in line]
with open(file, 'w') as file:
for line in lines:
file.write(line)
很好。