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
Seq
和List
)使用的传统yield!
来完成呢? - Luiso