F# 查询表达式 yield
F# query expressions yield
我正在学习 F#
,现在正在阅读与 SQL 类型提供程序一起使用的计算表达式和查询表达式。我当时正在做一些简单的任务,在某些时候需要连接 (Union) 2 个查询,我的第一个想法是,在阅读了有关 yield
in sequences 和 lists 的内容后,我的第一个想法是在这样的查询表达式中执行相同的操作:
query {
yield! for a in db.As select { // projection }
yield! for b in db.Bs select { // projection }
}
这是无效代码,然后我的第二种方法是 'concat' 他们使用:
seq {
yield! query {...}
yield! query {...}
}
或像这样使用 Linq 的 Concat
函数:(query {...}).Concat(query {...})
。如何做到这一点来自这个的回答
以上两种方法都有一个区别,使用 seq
将 运行 2 SQL 个查询,而 Concat
运行 仅一个是可以理解的。
我的问题是:为什么查询表达式不支持 yield
?
编辑:
经过进一步调查,我进入了 MSDN docs,我看到 Yield
和 YieldFrom
方法已实现,但没有 Combine
和 Delay
方法,这让我现在更加困惑
这是 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)
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
我正在学习 F#
,现在正在阅读与 SQL 类型提供程序一起使用的计算表达式和查询表达式。我当时正在做一些简单的任务,在某些时候需要连接 (Union) 2 个查询,我的第一个想法是,在阅读了有关 yield
in sequences 和 lists 的内容后,我的第一个想法是在这样的查询表达式中执行相同的操作:
query {
yield! for a in db.As select { // projection }
yield! for b in db.Bs select { // projection }
}
这是无效代码,然后我的第二种方法是 'concat' 他们使用:
seq {
yield! query {...}
yield! query {...}
}
或像这样使用 Linq 的 Concat
函数:(query {...}).Concat(query {...})
。如何做到这一点来自这个
以上两种方法都有一个区别,使用 seq
将 运行 2 SQL 个查询,而 Concat
运行 仅一个是可以理解的。
我的问题是:为什么查询表达式不支持 yield
?
编辑:
经过进一步调查,我进入了 MSDN docs,我看到 Yield
和 YieldFrom
方法已实现,但没有 Combine
和 Delay
方法,这让我现在更加困惑
这是 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)
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