我有两个序列(元组),我需要在其中进行连接:
进入序列(元组):
在C#中,我可以使用Linq Join扩展方法执行此操作,如:
seq1.Join(seq2, t => t.Item2, t=> t.Item1,
(t,u) => Tuple.Create(t.Item1, u.Item2))
如何在F#中完成此操作?我在那里找不到Seq的加入。
答案 0 :(得分:6)
编辑:实际上,您可以使用LINQ:
> open System.Linq;;
> let ans = seq1.Join(seq2, (fun t -> snd t), (fun t -> fst t), (fun t u -> (fst t, snd u)));;
为什么不使用F#的原生Seq
功能?如果你看at the docs和at this question,你可以简单地使用这些而不是LINQ。以Seq.map2
函数为例:
> let mapped = Seq.map2 (fun a b -> (fst a, snd b)) seq1 seq2;;
val it : seq<string * string> =
seq [("city1", "product1"); ("city2", "product2")]
应该为您提供所需内容,其中seq1
和seq2
是您的第一和第二序列。
答案 1 :(得分:3)
F#交互式会话:
> let seq1 = seq [("city1", "pin1"); ("city2", "pin2")];;
val seq1 : seq<string * string> = [("city1", "pin1"); ("city2", "pin2")]
> let seq2 = seq [("pin1", "product1"); ("pin2", "product2")];;
val seq2 : seq<string * string> = [("pin1", "product1"); ("pin2", "product2")]
> Seq.zip seq1 seq2;;
val it : seq<(string * string) * (string * string)> =
seq
[(("city1", "pin1"), ("pin1", "product1"));
(("city2", "pin2"), ("pin2", "product2"))]
> Seq.zip seq1 seq2 |> Seq.map (fun (x,y) -> (fst x, snd y));;
val it : seq<string * string> =
seq [("city1", "product1"); ("city2", "product2")]
此外,您必须能够对序列使用Linq查询,只需确保您具有对System.Linq程序集的引用并打开名称空间open System.Linq
UPDATE:在复杂场景中,您可以按如下方式使用序列表达式:
open System
let seq1 = seq [("city1", "pin1"); ("city2", "pin2"); ("city1", "pin3"); ("city1", "pin4")]
let seq2 = seq [("pin1", "product1"); ("pin2", "product2"); ("pin1", "product3"); ("pin2", "product1")]
let joinSeq = seq { for x in seq1 do
for y in seq2 do
let city, pin = x
let pin1, product = y
if pin = pin1 then
yield(city, product) }
for(x,y)in joinSeq do
printfn "%s: %s" x y
Console.ReadKey() |> ignore
答案 2 :(得分:3)
我认为你所期待的结果并不完全清楚,所以答案有点令人困惑。您的示例可以通过两种方式解释(无论是压缩还是加入),它们都是截然不同的。
压缩:如果您有两个相同长度的列表,并且您想对齐对应的项目(例如,第一个列表中的第一个项目与第二个列表中的第一个项目;第二个列表中的第一个项目第一个列表中的项目,第二个列表中的第二个项目等等。),然后查看使用List.zip
或List.map2
的答案。
但是,这意味着列表按引脚排序,引脚是唯一的。在这种情况下,您不需要使用Join
,甚至在C#/ LINQ中,您也可以使用Zip
扩展方法。
加入:如果列表可能有不同的长度,但引脚可能没有排序或不唯一,那么您需要编写一个真正的连接。 Artem K的代码的简化版本如下所示:
seq { for city, pin1 in seq1 do
for pin2, product in seq2 do
if pin1 = pin2 then yield city, product }
这可能比LINQ中的Join
效率低,因为它会遍历seq2
中seq1
中O(seq1.Length * seq2.Length)
中每个项目的所有项目,因此复杂度为Join
。我不确定,但我认为Join
可以使用一些哈希来提高效率。我可能会定义一个小帮手,而不是直接使用open System.Linq
module Seq =
let join (seq1:seq<_>) seq2 k1 k2 =
seq1.Join(seq2, (fun t -> k1 t), (fun t -> k2 t), (fun t u -> t, u))
方法:
(seq1, seq2)
||> Seq.join snd fst
|> Seq.map (fun (t, u) -> fst t, snd u)
然后你可以这样写:
zip
最后,如果您知道每个产品只有一个独特的城市(序列长度相同且两个引脚都是唯一的),那么您可以按引脚对两个序列进行排序,然后使用{{1 - 这可能比使用join
更有效(特别是如果你可以保持序列从一些早期的操作中排序)。