使用leftOuterJoin,.DefaultIfEmpty()是不必要的

时间:2014-09-24 03:30:32

标签: f# query-expressions

leftOuterJoin Query Expressions on MSDN的文档通过示例反复暗示,在使用leftOuterJoin .. on .. into ..时,您仍必须使用.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

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)}

1 个答案:

答案 0 :(得分:4)

简短版本:查询表达式使用空值。它是语言中的一个粗糙点,但却是一个可以容纳的语言。

我之前做过这个:

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))}

将每个结果包装在一个选项中(因为你不知道是否会有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))}