【问题标题】:In F# when should I use List.choose and when to use List.filter在 F# 中,我应该何时使用 List.choose 以及何时使用 List.filter
【发布时间】:2019-11-24 22:22:08
【问题描述】:

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

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

我很困惑 - 请有人解释一下何时最好使用“选择”以及何时最好使用“过滤器”?

【问题讨论】:

    标签: f#


    【解决方案1】:

    我想你已经观察到答案的本质了:filter 允许你测试一个条件,但是使用choose 你也可以将你的值投射到同一个表达式中,这需要一个单独的map如果使用filter

    由于问题陈述不明确(一个列表不能同时包含整数和字符串,除非它们被装箱;即列表的类型是obj list),我们可以看看这两种情况。使用 filter 时请注意额外的 map 函数。

    // 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 绑定。

    最终,两者之间的决定取决于您的个人喜好。

    【讨论】:

    • 完美 - 谢谢。根据您的第二个建议,该问题涉及一个盒装列表,但看到它全部完成并得到解释真的很有帮助。
    【解决方案2】:

    List.choose 严格来说比List.filter 更通用。您可以仅使用List.choose 来实现List.filter,但反之则不行。仅当您不能使用后者时才应使用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 calledfilterMap` 在许多语言和库中实现):

    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 可以引发异常,但这里不会,因为我们已经过滤掉了会导致该异常的 Nones。但是因为它不安全,很容易出错,而且由于这种实现效率不是很高,所以开箱即用的List.choose 很好。

    【讨论】:

      猜你喜欢
      • 2016-04-30
      • 2020-12-21
      • 1970-01-01
      • 2015-04-02
      • 1970-01-01
      • 2014-05-22
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      相关资源
      最近更新 更多