【问题标题】:F# List.mapi function with filtering带有过滤功能的 F# List.mapi 函数
【发布时间】:2016-03-24 04:11:41
【问题描述】:

我有这样一行代码:

list |> List.mapi (fun i x -> y, i)

(假设 y 已经定义了类型)

但我想返回带有某些条件的元素(例如过滤它)

我不能这样写:

 list |> List.mapi (fun i x -> if 'condition' then y, i)

因为它也需要其他条件,而我没有“其他”情况。 我也没有设法同时使用过滤器,因为我还需要返回正确的索引,如果我过滤列表,索引将被更改。 有什么想法吗?

编辑 到目前为止,我是这样实现的:

list |> List.mapi (fun i a -> if (a = None) then O, i else X, i) |> List.filter (fun (a,i) -> a = O)

对于其他情况,我给出了无用的 X,i,只是为了能够在此之后编写条件并删除 X 。它正在工作,这就是我想要的结果。但我确信有更好的解决方案。

【问题讨论】:

  • 你能提供更多的上下文吗?我认为您有一个xs 中的list 有点奇怪,您正在映射但在地图中根本不使用x。我认为,在更多的背景下,可能会有更好的选择。例如,如果您尝试将两个列表之间的元素关联起来,那么使用 zip 可能比使用索引更好。
  • 我需要 x 条件,比如如果 x = "someting"。谢谢,我会检查一下 zip 的作用
  • y 只是一个固定值还是您想要获取元素i 的另一个列表/集合?
  • y 只是固定值,所以我没有指定它
  • 很难准确地理解你想要做什么。请提供一些示例输入和相应的预期输出。

标签: list filter f#


【解决方案1】:

让我再补充一个答案: 从您的问题和 cmets 中,我了解到您希望根据值在保留原始索引的同时按条件过滤列表。 我不确定结果是否应该包含固定值列表和原始索引,或者您想要映射。以下允许两者:

let indexedFilterMap p f =
    List.indexed
    >> List.filter (snd >> p)
    >> List.map (fun (i, x) -> f x, i)
  1. 添加索引
  2. 使用索引列表的第二个值(即原始值)按 p 过滤
  3. 映射剩余的值并反转元组中的顺序(s.t. 它是值,索引)

如果您需要映射的索引(因为问题标题包括mapi):

let indexedFilterMapi p f =
    List.indexed
    >> List.filter (snd >> p)
    >> List.map f

或者如果您需要过滤器的索引:

let indexedFilteriMap p f =
    List.indexed
    >> List.filter p
    >> List.map (fun (i, x) -> f x, i)

组合应该简单明了。

然后可以使用这些:

let list = ['a'; 'b'; 'c']
let condition = (<>) 'b'
let y = "fixed value"

indexedFilterMap condition (fun _ -> y) list // [("fixed value", 0); ("fixed value", 2)]

let m (i, _) = sprintf "fixed value %i" i
indexedFilterMapi condition m list // ["fixed value 0"; "fixed value 2"]

let c (i, _) = i <> 1
indexedFilteriMap c (fun _ -> y) list // [("fixed value", 0); ("fixed value", 2)]

【讨论】:

    【解决方案2】:

    如果要过滤,但在过滤后应用严格单调递增顺序的索引,则先过滤,然后添加索引值:

    list |> List.filter condition |> List.mapi (fun i x -> x, i)
    

    这是一个示例,其中b 从按字母顺序排列的字符列表中被过滤掉:

    [('a', 0); ('c', 1); ('d', 2); ('e', 3); ('f', 4); ('g', 5); ('h', 6);
     ('i', 7); ('j', 8); ('k', 9); ('l', 10); ('m', 11); ('n', 12); ('o', 13)]
    

    【讨论】:

    • 实际上,我想要它 vica verca。 'c' 的索引应该是 2 而不是 1。过滤器不应该对索引做出反应
    • 如果我先映射然后过滤,我就不能写条件,因为这个条件已经是以前的列表了
    • @Kote 为什么不能先mapi,然后过滤?我上面示例中的mapi 函数实际上并没有任何事情,因此您可以轻松地反转表达式...
    • 马克,他不是从x 映射到x,而是从x 映射到某个固定值y。要使用mapfilter 来完成此操作,您需要mapi 来获得xifilter 用于x 条件和最终map 用于xy 一步。这就是我在回答中使用choose 的原因,因为它将最终的filtermap 滚动到列表中。
    【解决方案3】:

    首先,请注意,通过i 从列表中查找是一个 O(n) 操作,因此如果您正在这样做,则可以通过以不同方式表达问题来获得更有效的替代方案。

    对于描述的问题,您可以这样做:

    list 
    |> List.mapi (fun i x -> x, i)
    |> List.choose (fun (x,i) -> if 'condition on x' then Some (y,i) else None)
    

    返回 y 的元组列表和满足条件的元素索引。

    例子:

    考虑我从['a','b','c','d','e'] 开始,第一个mapi 将列表映射到[('a',0),('b',1),('c',2),('d',3),('e',4)],然后我应用choose 与(例如)选择元音并返回一些值y 的条件。我最终得到[(y,4)]

    编辑:为响应您的更新,这里有一个以您想要的方式使用它的示例。

    list 
    |> List.mapi (fun i x -> x, i)
    |> List.choose (fun (x,i) -> 
        match x with
        |O -> Some (O, i)
        |X -> None)
    

    【讨论】:

    • 这不是我想要的,因为条件只适用于初始列表,映射后,列表已经不同,条件无用
    • @Kote 不,事实并非如此。请注意,我在第一个映射中将x 映射到x,以便我仍然可以将条件应用于x。只有在choose 中,我才能将x 更改为y
    【解决方案4】:

    有很多方法可以实现,如果使用序列表达式就不需要编写else分支:

    let list = ['a';'b';'c';'d';'e']
    let condition x = x % 2 = 0
    
    [for i = 0 to List.length list - 1 do
        if condition i then yield (i, list.[i])]
    
    // val it : (int * char) list = [(0, 'a'); (2, 'c'); (4, 'e')]
    

    但请注意,按索引遍历列表在长列表中效率不高。所以,这取决于你在寻找什么,如果性能是必须的,我会改用List.fold

    list
        |> List.fold (fun (i, acc) e -> (i + 1, if condition i then (i, e)::acc else acc)) (0, [])
        |> snd
        |> List.rev // if you care about the order
    

    【讨论】:

      猜你喜欢
      • 2010-12-18
      • 1970-01-01
      • 2022-07-21
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 2016-07-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多