1

我刚开始学习 F#,并且对类型推断印象深刻,我想我会尝试一个从表中获取第一条记录的函数(使用查询表达式,Linq 样式):

let getfirst data = 
    let result = query { for n in data do take 1 }
    result |> Seq.head

这行得通,类型是IQueryable<'a> -> 'a.

但是为什么这个版本不起作用?

let getfirst2 data = 
    query { for n in data do head }

不应该像上次一样for n in data do head给出标量吗?'a有人可以解释为什么第二个版本不起作用,以及如何在不使用 Seq.head 的情况下使其工作吗?

4

2 回答 2

1

原因是查询构建器有一个Run用于运行查询的有点笨拙的重载方法,具有以下重载:

QueryBuilder.Run : Quotations.Expr<'t> -> 't
QueryBuilder.Run : Quotations.Expr<Linq.QuerySource<'t, IEnumerable>> -> seq<'t>
QueryBuilder.Run : Quotations.Expr<Linq.QuerySource<'t, IQueryable>> -> IQueryable<'t>

在您的情况下,任何重载都可以应用,只要给定一个合适的类型data(尽管QuerySource<_,_>是一种不打算由用户代码使用的类型,因此其中两个重载是不太可能的)。不幸的是,由于这些重载的定义方式很奇怪(第一个和第二个实际上是在单独的模块中定义的扩展方法),第三个赢得了重载解决之战。

于 2014-07-21T16:12:30.670 回答
1

我不知道为什么,但是当您将鼠标悬停在data参数上时,getfirst2您会看到它的类型System.Linq.IQueryable<Linq.QuerySource<'a, System.Linq.IQueryable>>实际上应该是System.Linq.IQueryable<'a>.

您可以通过添加类型注释来“修复”它:

open System.Linq
let getfirst2 (data : IQueryable<'a>) : 'a = query { 
    for item in data do
    head
}

然后它会像您预期的那样工作:

[1 .. 10]
|> System.Linq.Queryable.AsQueryable
|> getfirst2
|> printfn "%d" // Prints 1.

也许其他人可以解释为什么编译器会推断它所做的类型。

于 2014-07-19T19:28:43.700 回答