Как мне вызвать Enumerable.Join из F #? - PullRequest
5 голосов
/ 23 сентября 2010

У меня есть две последовательности (кортежей), для которых мне нужно сделать соединение:

  • Seq 1: [(City1 * Pin1), (City2 * Pin2), (City1 * Pin3), (City1 * Pin4)]
  • Seq 2: [(Pin1 * ProductA), (Pin2 * ProductB), (Pin1 * ProductC), (Pin2 * ProductA)]

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

  • [(City1 * ProductA), (City2 * ProductB), (City * ProductC), (City2 * Product A) ...]

В C # я мог бы сделать это, используя метод расширения Linq Join, например:

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

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

Ответы [ 3 ]

6 голосов
/ 23 сентября 2010

Редактировать: На самом деле, вы можете просто использовать 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? Если вы посмотрите на документы и на этот вопрос , вы можете просто использовать их вместо 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 - это ваша первая и вторая последовательности.

3 голосов
/ 24 сентября 2010

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

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

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

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

    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 (особенно если вы можете сохранить последовательность отсортированной по некоторым более ранним операциям).

3 голосов
/ 23 сентября 2010

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

ОБНОВЛЕНИЕ: в сложном сценарии вы можете использовать выражения последовательности следующим образом:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...