2014-09-24 2 views
4

Документация leftOuterJoinQuery Expressions on MSDN неоднократно подразумевает через образцы, которые при использовании leftOuterJoin .. on .. into .., что вы все равно должны использовать .DefaultIfEmpty() для достижения желаемого эффекта.С leftOuterJoin, .DefaultIfEmpty() ненужно

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

type Test = A | B | C 
let G = [| A; B; C|] 
let H = [| A; C; C|] 

printfn "%A" <| query { 
    for g in G do 
    leftOuterJoin h in H on (g = h) into I 
    for i in I.DefaultIfEmpty() do 
    select (g, i)} 

printfn "%A" <| query { 
    for g in G do 
    leftOuterJoin h in H on (g = h) into I 
    for i in I do 
    select (g, i)} 

// seq [(A, A); (B, null); (C, C); (C, C)] 
// seq [(A, A); (B, null); (C, C); (C, C)] 

1) Можете ли вы подтвердить это?

Если это так, я понял это только после написания этого альтернативного увеличения типа, чтобы лучше справляться с непревзойденными результатами, и я был удивлен, увидев в моем выходе null s!

type IEnumerable<'TSource> with 
    member this.NoneIfEmpty = if (Seq.exists (fun _ -> true) this) 
           then Seq.map (fun e -> Some e) this 
           else seq [ None ] 

printfn "%A" <| query { 
    for g in G do 
    leftOuterJoin h in H on (g = h) into I 
    for i in I.NoneIfEmpty do 
    select (g, i)} 

// seq [(A, Some A); (B, Some null); (C, Some C); (C, Some C)] 

2) Есть ли способ, чтобы получить None вместо null/Some null от leftOuterJoin?

3) То, что я действительно хочу сделать, это выяснить, есть ли какие-либо непревзойденную g

printfn "%A" <| query { 
    for g in G do 
    leftOuterJoin h in H on (g = h) into I 
    for i in I.NoneIfEmpty do 
    where (i.IsNone) 
    exists (true) } 

Я полагал, что это следующий из, но это не очень F #:

printfn "%A" <| query { 
    for g in G do 
    leftOuterJoin h in H on (g = h) into I 
    for i in I do 
    where (box i = null) 
    exists (true)} 

ответ

3

Short version: Query Expressions использует nulls. Это грубое место в языке, но сдержанное.

Я сделал это раньше:

let ToOption (a:'a) = 
    match obj.ReferenceEquals(a,null) with 
    | true -> None 
    | false -> Some(a) 

Это позволит вам сделать:

printfn "%A" <| query { 
    for g in G do 
    leftOuterJoin h in H on (g = h) into I 
    for i in I do 
    select (g,(ToOption i))} 

Какие обертывания каждый результат в качестве опции (так как вы не знаете, если там будет Я хочу отметить, что F # использует null для представления None во время выполнения в качестве оптимизации. Поэтому, чтобы убедиться, что это действительно то, что вы хотите, принять решение по опции, например:

Seq.iter (fun (g,h) -> 
       printf "%A," g; 
       match h with 
       | Some(h) -> printfn "Some (%A)" h 
       | None -> printfn "None") 
    <| query { 
    for g in G do 
    leftOuterJoin h in H on (g = h) into I 
    for i in I do 
    select ((ToOption g),(ToOption i))} 
+1

Удивительный, спасибо. Итак, обратно к 1) является ли документация действительно испорченной? Я бы позаботился об этом. –

+0

Хм ... как вы думаете, должно быть поведение? –

+1

После небольшого исследования в [DefaultIfEmpty] (http://msdn.microsoft.com/en-us/library/vstudio/bb355419 (v = vs.110) .aspx), он ничего не делает, поскольку 'I' является сплющенные. –

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