根据 F# 中的条件重新格式化列表

Reformatting list based on a condition in F#

我是函数式编程的新手,正在使用 F# 开发一个项目。

我 运行 遇到了一个问题:我有一个 string list list 类型的列表,我需要根据每个 string list 的中间元素构建单独的列表。例如:

[["1";"b";"2"];["2";"a";"0"];["3";"b";"4"];["3";"a";"5"]]

将分成 2 个类似于以下的列表:

let a = [["2";"0"];["3";"5"]]
let b = [["1";"2"];["3";"4"]]

我尝试使用 let a = [for [x;y;z] in myList do yield [x;z]],但在添加 y = "b" 的条件时遇到了问题。

如有任何帮助,我们将不胜感激

let myList = [["1";"b";"2"];["2";"a";"0"];["3";"b";"4"];["3";"a";"5"]]

let a = [for [x;y;z] in myList do if y="a" then yield [x;z]]
let b = [for [x;y;z] in myList do if y="b" then yield [x;z]]

您正在尝试按中间元素拆分列表。当您的列表没有 3 个元素时,预期的行为是什么?

在 Functional_S 提供的答案中,您会看到

[x;y;z] 下的波浪线
let a = [for [x;y;z] in myList do if y="a" then yield [x;z]]

编译器说 "Incomplete pattern matches"。与其现在添加额外的检查来处理空列表、长度为 2 的列表等,不如考虑更改数据类型的设计。如果您的数据始终包含 3 个元素,则使用恰好包含 3 个元素的数据结构。元组在这里是一个明显的选择,或者使用记录。

let myList = [ ("1","b","2"); ("2","a","0"); ("3","b","4"); ("3","a","5") ]
let splitByMiddle = 
    myList 
    |> List.groupBy (fun (_, middle, _) -> middle)
    |> List.map (fun (middle, elems) -> middle, elems |> List.map (fun (l, _, r) -> l, r))

如果你以交互方式执行它,你会得到:

val splitByMiddle : (string * (string * string) list) list =
    [("b", [("1", "2"); ("3", "4")]); ("a", [("2", "0"); ("3", "5")])]

另一种选择是:

let splitByMiddle = 
    myList 
    |> List.map (fun (l, middle, r) -> middle, (l, r))
    |> List.groupBy fst
    |> List.map (fun (middle, elems) -> middle, elems |> List.map snd)

我发现当您使用您的数据类型尽可能接近地为您的域建模时,F# 确实处于最佳性能。在像 Matlab 这样的语言中,向量和矩阵是你的头等大事,你会把所有东西都放在列表中。但在 F# 中,定义数据类型的成本非常低(就键入工作量而言)- 一旦您这样做了,编译器就是您最好的朋友,它会提醒您代码未涵盖的可能极端情况。

鉴于此:我看到您所有的中间元素都是字符串,而 left/right 元素是整数。也许您的域更适合此记录?

type R = 
    { 
        Left: int
        Right: int
        Middle: string
    }
let create (l, m, r) = { Left = l; Right = r; Middle = m}
let myList = [ create(1,"b",2); create(2,"a",0); create(3,"b",4); create(3,"a",5) ]
let splitByMiddle = 
    myList 
    |> List.groupBy (fun r -> r.Middle)

这会给你:

val splitByMiddle : (string * R list) list =
  [("b", [{Left = 1;
       Middle = "b";
       Right = 2;}; {Left = 3;
                     Middle = "b";
                     Right = 4;}]); ("a", [{Left = 2;
                                            Middle = "a";
                                            Right = 0;}; {Left = 3;
                                                          Middle = "a";
                                                          Right = 5;}])]