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

Fsh*_*ete 4 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
            ()
Run Code Online (Sandbox Code Playgroud)

如何在功能上做得更好?

Sør*_*ois 7

和你的想法一样,只是稍微清楚一些.我将你的"标准"提炼成了一个参数函数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)
Run Code Online (Sandbox Code Playgroud)

你会这样使用它:

find (=) [1;2;3;4] [1;3] (* Prints 1,1 -- 2 -- 3,3 -- 4. *)
Run Code Online (Sandbox Code Playgroud)

  • 我不认为它保证任何东西,并且通常很难确定(因为LINQ实现没有打开),但它*很可能*在里面某处使用`ILookup/Lookup`和/或`IGrouping`.在这种情况下,"哈希加入"式的实施应该不会太难,应该非常快,所以我敢打赌我的钱.但这都是猜测. (2认同)

Mar*_*ann 5

简短回答

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
Run Code Online (Sandbox Code Playgroud)

更长的答案

一般来说,当您需要将一个列表中的每个元素与另一个列表中的每个元素进行比较时,您将需要一个笛卡尔积,您可以如下定义:

let crossJoin xs ys =
    xs
    |> Seq.map (fun x -> ys |> Seq.map (fun y -> (x, y)))
    |> Seq.concat
Run Code Online (Sandbox Code Playgroud)

例如,如果您有:

let integers = [0 .. 10] |> List.toSeq
let strings = [0 .. 5] |> Seq.map string
Run Code Online (Sandbox Code Playgroud)

笛卡尔积为:

> 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")]
Run Code Online (Sandbox Code Playgroud)

给定该crossJoin函数,您可以轻松地根据比较函数对这些元组进行分组:

let group f xs ys = ys |> crossJoin xs |> Seq.groupBy f
Run Code Online (Sandbox Code Playgroud)

例子:

> 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"); ...])]
Run Code Online (Sandbox Code Playgroud)

现在您有一个元组序列,其中元组中的第一个值是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")]
Run Code Online (Sandbox Code Playgroud)

然后你可以简单地使用Seq.iterSeq.map来执行该操作。

请注意,这使用了惰性求值请注意,这自始至终都

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