如何在 F# 中编写查询表达式?

编程入门 行业动态 更新时间:2024-10-22 18:38:54
本文介绍了如何在 F# 中编写查询表达式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我一直在查看这里的查询表达式 msdn.microsoft/en-us/library/vstudio/hh225374.aspx

我一直想知道为什么以下内容是合法的

让 testQuery = 查询 {为 netflix.Titles 中的数字做where (number.Name.Contains("Test"))}

但你真的不能做这样的事情

let ChristmasPredicate = fun (x:Catalog.ServiceTypes.Title) ->x.Name.Contains("圣诞节")让 testQuery = 查询 {为 netflix.Titles 中的数字做其中圣诞节谓词}

当然,F# 允许这样的可组合性,因此您可以重用谓词??如果我想要圣诞节标题与另一个谓词相结合,比如在特定日期之前怎么办?我必须复制并粘贴我的整个查询?C# 与此完全不同,它有多种构建和组合谓词的方法

解决方案

这对于需要显式引用的 F# 2.0 版本的查询非常容易(我写了一个 关于它的博文).有一种方法可以在 C# 中实现类似的东西(另一个博客文章),我认为类似的技巧可能是玩过 F# 3.0.

如果您不介意更丑陋的语法,那么您也可以在 F# 3.0 中使用显式引号.当你写query { .. } 编译器实际上生成如下内容:

query.Run()

其中 <@ .. @> 中的代码是引用的 F# 代码 - 也就是说,代码存储在表示源代码的 Expr 类型中,并且可以转换为 LINQ 表达式,从而转换为 SQL.

这是我使用 SqlDataConnection 类型提供程序测试的示例:

let db = Nwind.GetDataContext()让谓词 = <@ fun (p:Nwind.ServiceTypes.Products) ->p.UnitPrice.Value >50.0M @>让测试 () =<@query.Select( query.Where(query.Source(db.Products), %predicate),有趣的 p ->p.ProductName) @>|>查询.运行|>Seq.iter (printfn "%s")

关键技巧是,当您使用显式引用(使用 <@ .. @>)时,您可以使用 % 运算符进行引用切片.这意味着 predicate 的引用被放入查询的引用(在 test 中)你写 %predicate 的地方.

与漂亮的查询表达式相比,代码相当丑陋,但我怀疑您可以通过在此之上编写一些 DSL 或通过预处理引用来使其更好.

稍加努力,实际上可以再次使用 query { .. } 语法.您可以引用整个查询表达式并编写 <@ query { .. } @> - 这不会直接起作用,但您可以使用引用并提取查询的实际主体,然后将其直接传递给 query.Run.以下是适用于上述示例的示例:

打开 System.Linq打开 Microsoft.FSharp.Quotations打开 Microsoft.FSharp.Quotations.Patterns让 runQuery (q:Expr) =匹配 q|应用程序(Lambda(builder, Call(Some builder2, miRun, [Quote body])), queryObj) ->query.Run(Expr.Cast(body))|_ ->以错误的论点"失败让测试 () =<@ query { for p in db.Products do其中 ((%谓词) p)选择 p.ProductName } @>|>运行查询|>Seq.iter (printfn "%s")

I've been looking at query expressions here msdn.microsoft/en-us/library/vstudio/hh225374.aspx

And I've been wondering why the following is legitimate

let testQuery = query { for number in netflix.Titles do where (number.Name.Contains("Test")) }

But you can't really do something like this

let christmasPredicate = fun (x:Catalog.ServiceTypes.Title) -> x.Name.Contains("Christmas") let testQuery = query { for number in netflix.Titles do where christmasPredicate }

Surely F# allows composability like this so you can reuse a predicate?? What if I wanted Christmas titles combined with another predicate like before a specific date? I have to copy and paste my entire query? C# is completely unlike this and has several ways to build and combine predicates

解决方案

This was quite easy to do with the F# 2.0 version of queries which required explicit quotations (I wrote a blog post about it). There is a way to achieve similar thing in C# (another blog post) and I think similar tricks could be played with F# 3.0.

If you do not mind uglier syntax, then you can use explicit quotations in F# 3.0 too. When you write query { .. } the compiler actually generates something like:

query.Run(<@ ... @>)

where the code inside <@ .. @> is quoted F# code - that is, code stored in an Expr type that represents the source code and can be translated to LINQ expressions and thus to SQL.

Here is an example that I tested with the SqlDataConnection type provider:

let db = Nwind.GetDataContext() let predicate = <@ fun (p:Nwind.ServiceTypes.Products) -> p.UnitPrice.Value > 50.0M @> let test () = <@ query.Select ( query.Where(query.Source(db.Products), %predicate), fun p -> p.ProductName) @> |> query.Run |> Seq.iter (printfn "%s")

The key trick is that, when you use explicit quotations (using <@ .. @>) you can use the % operator for quotation slicing. This means that the quotation of predicate is put into the quotation of the query (in test) in place where you write %predicate.

The code is quite ugly compared to the nice query expression, but I suspect you could make it nicer by writing some DSL on top of this or by pre-processing the quotation.

EDIT: With a bit more effort, it is actually possible to use the query { .. } syntax again. You can quote the entire query expression and write <@ query { .. } @> - this will not directly work, but you can then take the quotation and extract the actual body of the query and pass it to query.Run directly. Here is a sample that works for the above example:

open System.Linq open Microsoft.FSharp.Quotations open Microsoft.FSharp.Quotations.Patterns let runQuery (q:Expr<IQueryable<'T>>) = match q with | Application(Lambda(builder, Call(Some builder2, miRun, [Quote body])), queryObj) -> query.Run(Expr.Cast<Microsoft.FSharp.Linq.QuerySource<'T, IQueryable>>(body)) | _ -> failwith "Wrong argument" let test () = <@ query { for p in db.Products do where ((%predicate) p) select p.ProductName } @> |> runQuery |> Seq.iter (printfn "%s")

更多推荐

如何在 F# 中编写查询表达式?

本文发布于:2023-11-17 03:09:35,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1608597.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:表达式   如何在

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!