Выражения запросов F# дают
Я учусь F#
и сейчас я читаю о выражениях вычислений и выражениях запросов для использования с поставщиками типов SQL. Я выполнял несколько простых задач, и в какой-то момент мне нужно было объединить (Union) 2 запроса, моя первая мысль после прочтения о yield
в последовательностях и списках нужно было сделать то же самое внутри выражения запроса следующим образом:
query {
yield! for a in db.As select { // projection }
yield! for b in db.Bs select { // projection }
}
это был неверный код, тогда мой второй подход заключался в том, чтобы "конкатить" их, используя:
seq {
yield! query {...}
yield! query {...}
}
или используя Linq's Concat
функционировать так: (query {...}).Concat(query {...})
, Как это сделать пришло из ответа на этот вопрос
Оба вышеупомянутых подхода работают с одним отличием, хотя, используя seq
будет выполнять 2 запроса SQL, а Concat
работает только тот, который понятен.
Мой вопрос: почему нет yield
поддерживается в выражениях запросов?
РЕДАКТИРОВАТЬ:
После дальнейшего расследования я добрался до документов MSDN и увидел Yield
а также YieldFrom
методы реализованы, но не Combine
а также Delay
методы, что сейчас еще более запутанно для меня
2 ответа
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#: 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)