F#查询表达式产生

时间:2015-10-19 16:13:23

标签: f# f#-3.0 computation-expression query-expressions

我正在学习F#,现在正在阅读有关与SQL类型提供程序一起使用的计算表达式和查询表达式。我正在做一些简单的任务,并且在某些时候需要连接(Union)2个查询,我的第一个想法是,在阅读序列和列表中的yield后,在这样的查询表达式中做同样的事情:

query {
    yield! for a in db.As select { // projection }
    yield! for b in db.Bs select { // projection }
}

这是无效的代码,然后我的第二种方法是“连续”#39;他们使用:

seq {
    yield! query {...}
    yield! query {...}
}

或使用Linq的Concat函数,如下所示:(query {...}).Concat(query {...})。如何做到这一点来自question的回答

上述两种方法都有一个不同之处,使用seq将运行2个SQL查询,而Concat只运行一个可理解的方法。

我的问题是:为什么查询表达式不支持yield

修改

经过进一步调查后,我到了MSDN docs,我看到了YieldYieldFrom方法,但没有实现CombineDelay方法,现在让我更加困惑

2 个答案:

答案 0 :(得分:3)

yield!在某种程度上支持查询,并且可以在select通常使用的地方使用:

query { 
    for x in [5;2;0].AsQueryable() do 
    where (x > 1)
    sortBy x
    yield! [x; x-1] 
 } |> Seq.toList // [2;1;5;4]

但是,一般来说,你不能随意散布查询和序列操作,因为很难定义它们应该如何构成:

query {
    for x in [1;2;3] do
    where (x > 1)
    while true do // error: can't use while (what would it mean?)
    sortBy x 
}

同样地:

query {
    for x in [1;2;3] do
    where (x > 1)
    sortBy x 
    yield! ['a';'b';'c']
    yield! ['x';'y';'z'] // error
}        

这有点含糊不清,因为它不清楚第二个yield!是否在for循环内,或者之后是否附加了一组元素。

所以最好将查询视为查询,将序列视为序列,即使两种计算表达式都支持某些相同的操作。

通常,查询自定义运算符在元素方面是有效的,因此表达诸如联合或连接之类的东西很尴尬,因为它们处理整个集合而不是单个元素。但是,如果您愿意,可以创建一个查询构建器,该构建器添加了一个concat自定义运算符,该运算符采用了序列,但它可能会感觉有点不对称:

open System.Linq

type QB() =
    member inline x.Yield v = (Seq.singleton v).AsQueryable()
    member inline x.YieldFrom q = q
    [<CustomOperation("where", MaintainsVariableSpace=true)>]
    member x.Where(q:IQueryable<_>, [<ProjectionParameter>]c:Expressions.Expression<System.Func<_,_>>) = q.Where(c)
    [<CustomOperation("sortBy", MaintainsVariableSpace=true)>]
    member x.SortBy(q:IQueryable<_>, [<ProjectionParameter>]c:Expressions.Expression<System.Func<_,_>>) = q.OrderBy(c)
    [<CustomOperation("select")>]
    member x.Select(q:IQueryable<_>, [<ProjectionParameter>]c:Expressions.Expression<System.Func<_,_>>) = q.Select(c)
    [<CustomOperation("concat")>]
    member x.Concat(q:IQueryable<_>, q') = q.Concat(q')
    member x.For(q:IQueryable<'t>, c:'t->IQueryable<'u>) = q.SelectMany(fun t -> c t :> seq<_>) // TODO: implement something more reasonable here

let qb = QB()

qb {
    for x in ([5;2;0].AsQueryable()) do
    where (x > 1)
    sortBy x
    select x
    concat ([7;8;9].AsQueryable())
} |> Seq.toList

答案 1 :(得分:1)

这是F#中https://msdn.microsoft.com/en-us/library/hh225374.aspx

中查询表达式的绝佳参考

特别是,我认为该页面中的以下示例可以满足您的需求:

let query1 = query {
        for n in db.Student do
        select (n.Name, n.Age)
    }

let query2 = query {
        for n in db.LastStudent do
        select (n.Name, n.Age)
        }

query2.Union (query1)