2010-09-23 4 views
5

У меня есть две последовательности (кортежей), на которых мне нужно сделать присоединиться:Как я могу вызвать Enumerable.Join из F #?

  • Seq 1: [(City1 * Pin1), (City2 * Pin2), (City1 * Pin3), (City1 * Pin4)]
  • СтартПослед 2: [(Pin1 * ProductA), (Pin2 * ProductB), (Pin1 * ProductC), (Pin2 * ProductA)]

в последовательность (кортежей):

  • [(City1 * ProductA), (City2 * ProductB), (City * ProductC), (City2 * Продукт А) ...]

В C# я мог бы сделать это с помощью Linq Регистрация метод расширения как:

seq1.Join(seq2, t => t.Item2, t=> t.Item1, 
    (t,u) => Tuple.Create(t.Item1, u.Item2)) 

Как сделать это в F #? Я не могу найти участие в Seq.

+0

Я ищу эффективную реализацию, так как этот список продуктов/контактов довольно длинный. Linq, похоже, отлично работает для меня, но не может заставить его работать с f #. –

+0

Выполняют ли последовательности F # «IEnumerable»? Если они этого не делают, вы не можете использовать LINQ. –

+0

Да, они реализуют IEnumerable. –

ответ

6

Edit: На самом деле, вы можете просто использовать 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 # 's родной 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 ваши первые и вторые последовательности.

+0

Города будут один для многих с булавкой и булавками, которые будут многими для многих с продуктами. Не могли бы вы объяснить, как это будет работать? –

+0

Вы имеете в виду, что вы могли бы «[(City1 * Pin1 * Pin2), (City2 * Pin2)]' и '[(Pin1 * ProductA), (Pin2 * ProductB * Productc)], т.е. используя кортежи, которые более 2 элементы? –

+0

Нет, я имею в виду, что я мог бы иметь несколько элементов в последовательности с тем же городом и с другим булавкой. Точно так же я мог бы иметь несколько элементов с одним и тем же выводом и другим продуктом или наоборот в seq 2. У кортежа всегда будет 2 предмета. –

2

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 
+0

3 секунды передо мной! Должны не добавлять ссылки ... –

+0

Города имели бы более одного штыря, а штыри были бы многими для многих с продуктами. –

+0

Итак, что делает последняя команда: создайте список пар кортежей, как показано предыдущей командой (Seq.zip), затем сопоставьте этот список с новым, получив первый элемент первого кортежа (fst x) и второй элемент второго кортежа (snd y). –

2

Я думаю, что не совсем ясно, какие результаты вы ожидаете, поэтому ответы немного запутанны. Ваш пример может быть интерпретирован двумя способами (либо как zipping, либо как , присоединившись к), и они сильно отличаются друг от друга.

  • Архивирование: Если у вас есть два списка одинаковой длины, и вы хотите выровнять correspoding элементы (например первый элемент из первого списка с 1-го элемента из второго списка; 2-й элемент из первого списка с 2 пункт из второго списка и т. д.), затем посмотрите ответы, которые используют либо List.zip, либо List.map2.

    Однако это означало бы, что списки сортируются по штырькам, а контакты уникальны.В этом случае вам не нужно использовать Join и даже в C#/LINQ, вы можете использовать метод расширения Zip.

  • Присоединение: Если списки могут иметь разную длину, контакты не могут быть отсортированы или не уникальны, тогда вам нужно написать реальное соединение. Упрощенная версия кода Артем K будет выглядеть следующим образом:

    seq { for city, pin1 in seq1 do 
         for pin2, product in seq2 do 
          if pin1 = pin2 then yield city, product } 
    

    Это может быть менее эффективным, чем Join в LINQ, потому что она перебирает все элементы в 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 - это может быть более эффективным, чем использование join (особенно если вы можете сохранитьпоследовательность, отсортированная по некоторым более ранним операциям).

+0

Привет, я хотел присоединиться (перекрестный продукт) и его реализовать с помощью словаря в LINQ, что делает его быстрее. –

Смежные вопросы