F#比较两个列表,采取不同的行动

时间:2014-03-19 12:29:26

标签: f# c#-to-f#

如何惯用这样做:给定一个东西列表,查找其中的项目是否符合另一个列表中的条件,如果它确实执行了一个操作,如果它没有执行其他操作。我看到代码在C#中执行此操作,我想知道如何在F#中执行此操作。

这是代码,因为它将在F#中强制实现:

let find list1 list2 =
    for i in list1 do
        let mutable found = false
        for k in list2 do
            if i=k then    //if the criteria is equals
                found <- true
                //do task using k & i
        if found =false then   
            //do other task using i
            ()

如何在功能上做得更好?

3 个答案:

答案 0 :(得分:7)

与您的想法相同,只有略微更清晰。我提炼了你的标准&#34;进入参数函数f : 'a -> 'b -> bool

let find f xs ys = 
    xs |> List.map (fun x -> (x, List.tryFind (f x) ys))
       |> List.iter (function (x, None) -> printfn "%A" x
                            | (x, Some y) -> printfn "%A,%A" x y)

您可以这样使用它:

find (=) [1;2;3;4] [1;3] (* Prints 1,1 -- 2 -- 3,3 -- 4. *)

答案 1 :(得分:4)

简短回答

let crossJoin xs ys =
    xs
    |> Seq.map (fun x -> ys |> Seq.map (fun y -> (x, y)))
    |> Seq.concat
let group f xs ys = ys |> crossJoin xs |> Seq.groupBy f

更长的答案

通常,当您需要将一个列表中的每个元素与另一个列表中的每个元素进行比较时,您需要一个笛卡尔积,您可以这样定义:

let crossJoin xs ys =
    xs
    |> Seq.map (fun x -> ys |> Seq.map (fun y -> (x, y)))
    |> Seq.concat

例如,如果你有:

let integers = [0 .. 10] |> List.toSeq
let strings = [0 .. 5] |> Seq.map string
笛卡尔积将是:

> crossJoin integers strings |> Seq.toList;;
val it : (int * string) list =
  [(0, "0"); (0, "1"); (0, "2"); (0, "3"); (0, "4"); (0, "5"); (1, "0");
   (1, "1"); (1, "2"); (1, "3"); (1, "4"); (1, "5"); (2, "0"); (2, "1");
   (2, "2"); (2, "3"); (2, "4"); (2, "5"); (3, "0"); (3, "1"); (3, "2");
   (3, "3"); (3, "4"); (3, "5"); (4, "0"); (4, "1"); (4, "2"); (4, "3");
   (4, "4"); (4, "5"); (5, "0"); (5, "1"); (5, "2"); (5, "3"); (5, "4");
   (5, "5"); (6, "0"); (6, "1"); (6, "2"); (6, "3"); (6, "4"); (6, "5");
   (7, "0"); (7, "1"); (7, "2"); (7, "3"); (7, "4"); (7, "5"); (8, "0");
   (8, "1"); (8, "2"); (8, "3"); (8, "4"); (8, "5"); (9, "0"); (9, "1");
   (9, "2"); (9, "3"); (9, "4"); (9, "5"); (10, "0"); (10, "1"); (10, "2");
   (10, "3"); (10, "4"); (10, "5")]

鉴于crossJoin函数,您可以根据比较函数轻松对这些元组进行分组:

let group f xs ys = ys |> crossJoin xs |> Seq.groupBy f

示例:

> group (fun (x, y) -> x.ToString() = y) integers strings |> Seq.toList;;
val it : (bool * seq<int * string>) list =
  [(true, seq [(0, "0"); (1, "1"); (2, "2"); (3, "3"); ...]);
   (false, seq [(0, "1"); (0, "2"); (0, "3"); (0, "4"); ...])]

现在你有了一系列元组,其中元组中的第一个值是truefalse

例如,如果您想对所有匹配项执行特定操作,则可以解压缩组:

> group (fun (x, y) -> x.ToString() = y) integers strings |> Seq.filter fst |> Seq.head |> snd |> Seq.toList;;
val it : (int * string) list =
  [(0, "0"); (1, "1"); (2, "2"); (3, "3"); (4, "4"); (5, "5")]

然后,您只需使用Seq.iterSeq.map即可执行操作。

请注意,它一直使用延迟评估

除非您的实际操作涉及操纵共享状态,否则您手头有一个令人尴尬的并行解决方案,因此您可以轻松地将工作分散到多个处理器上。

答案 2 :(得分:0)

这是我提出的解决方案:

let findF list1 list2 =
    list1 
    |> List.iter  (fun i ->  match (List.tryFind (fun k -> i=k) list2)  with
                         | Some(k') -> printfn "i,k=%i,%i" i k'  
                         | None     -> printfn "i=%i" i   )      

编辑:更新了评论更正。