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

F# 比较两个列表,采取不同的行动,f#,c#-to-f#,F#,C# To F#,如何惯用地做到这一点:给定一个物品列表,找出其中的一个物品是否符合另一个列表中的标准,如果它做了一个动作,如果它没有做另一个动作。我在C语言中看到过这样的代码,我想知道在F语言中该怎么做 下面是在F#中必须实现的代码: 让我们查找列表1列表2= 因为我在清单1中是这样做的 设mutable found=false 对于清单2中的k,请执行以下操作: 如果i=k,那么//如果条件等于 发现这就是我提出的解决方案: let findF list1 list2 = list1 |&g

如何惯用地做到这一点:给定一个物品列表,找出其中的一个物品是否符合另一个列表中的标准,如果它做了一个动作,如果它没有做另一个动作。我在C语言中看到过这样的代码,我想知道在F语言中该怎么做

下面是在F#中必须实现的代码:

让我们查找列表1列表2=
因为我在清单1中是这样做的
设mutable found=false
对于清单2中的k,请执行以下操作:
如果i=k,那么//如果条件等于

发现这就是我提出的解决方案:

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   )      
编辑:使用注释中的更正进行更新

和你的想法一样,只是稍微清楚一点。我将你的“标准”提炼成一个参数函数
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. *)
let crossJoin xs ys =
    xs
    |> Seq.map (fun x -> ys |> Seq.map (fun y -> (x, y)))
    |> Seq.concat

简短回答

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 group f xs ys = ys |> crossJoin xs |> Seq.groupBy f
一般来说,当您需要将一个列表中的每个元素与另一个列表中的每个元素进行比较时,您需要一个笛卡尔积,您可以这样定义:

find (=) [1;2;3;4] [1;3] (* Prints 1,1 -- 2 -- 3,3 -- 4. *)
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"); ...])]
然后您可以简单地使用
Seq.iter
Seq.map
执行操作

请注意,这始终使用惰性评估


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

这实际上不起作用,因为在
将k与
匹配时,
k
是未定义的。您可能想将
List.tryFind…
match k…
替换为
match List.tryFind(=)i)替换为…
@SørenDebois谢谢,我现在已经更正了错误。请注意。根据这些列表的长度,从性能的角度来看(如果我们需要改进的话)使用它可能会“更好”,这类似于
左连接
。可读性可能会受到一些影响:)可能。我假设函数应该适用于任意条件。不过,我不知道GroupJoin保证了O(n lgm)的运行时间。是吗?我不认为它能保证任何事情,而且通常很难确定(因为LINQ实现没有打开),但它很可能在内部某处使用
ILookup/Lookup
和/或
IGrouping
在这种情况下,类似Hash-join的实现应该不会太难,而且应该相当快,所以我敢打赌。但这都是猜测。很有趣,谢谢。关于性能问题,我应该补充一点,我的答案是“惯用的”,即我们使用线性空间而不担心它。如果(空间)性能是一个问题,应该用
Seq.Map
替换
List.Map
。既然我回家了,我可以加上我的2美分:一个(左)
外套
,这是一个简单的实现,应该足够好了。不用说散列,但它应该能起到作用。对于
交叉连接
关于xs |>Seq.collect(fun x->ys |>Seq.map(fun y->(x,y)))
?我喜欢这个答案中的函数如何分解成更小的部分,以便可以在其他地方重用。@mydogisbox是的,您的
交叉连接
替代方案也可以。它的作用相同,但略短。我想我将开始使用这个变体。谢谢,太好了。如果操作符
f
有副作用,并且必须严格按照
xs
列表的顺序应用,那么这个解决方案会是什么样子?@SørenDebois为什么比较函数会有副作用?