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

时间:2016-03-02 21:47:47

标签: f# functional-programming f#-4.0

我是函数式编程的新手,正在研究F#中的项目。

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

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

将分为两个类似于以下内容的列表:

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”的条件时遇到了问题。

非常感谢任何帮助

2 个答案:

答案 0 :(得分:2)

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]]

答案 1 :(得分:1)

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

在Functional_S提供的答案中,您会看到<{1}}下的摇摆线

[x;y;z]

编译器说“不完整模式匹配”。现在考虑更改数据类型的设计,而不是现在添加额外的检查来处理空列表,长度为2的列表等。如果您的数据始终包含3个元素,则使用具有3个元素的数据结构。元组在这里是一个明显的选择,或使用记录。

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

如果你在互动中执行,你会得到:

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")])]

我发现当您使用数据类型尽可能地模拟域时,F#确实处于最佳性能。在像Matlab这样的语言中,矢量和matrics是你的头号工作马,你可以将所有内容放入列表中。但是在F#中,定义数据类型的成本非常低(在打字工作方面) - 一旦你这样做了,编译器就是你最好的朋友,提醒你代码没有覆盖的可能的极端情况。

从那个角度来看:我看到你所有的中间元素都是字符串,而左/右元素是整数。也许您的域名更好地通过此记录建模?

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)

这会给你:

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)