在F#中,何时应使用List.choose以及何时应使用List.filter

时间:2019-11-24 13:16:35

标签: f#

我刚刚进行了CodeWars练习-“创建一个函数,该函数接受非负整数和字符串的列表,并返回过滤出字符串的新列表”。

我的解决方案使用List.filter,但在其中一种极端情况下失败了。因此,我查看了他们的解决方案,并使用了List.choose-它似乎与我的版本几乎相同,只是在决定是否将结果包含在新列表中之前将结果转换为选项。

我很困惑-有人可以解释什么时候最好使用“选择”,什么时候最好使用“过滤器”?

2 个答案:

答案 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.mapOption.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) 很好。