F#如何与while循环一起使用

9
我有一个数据读取器,我想从中返回一组行,经过大约一天的阅读书籍后,我无法找到在F#中完成最佳方法。我可以用普通的C#方法在F#中完成,但那不是我使用F#的原因。
这是我想要实现的:
let values =
    while reader.Read() do
        yield reader.GetString(0), reader.GetInt64(1)

以下是我尝试的方法:
- 所有值都被收集到values中,它可以是字典、元组或任何集合 - yield不能在while循环中使用,但这就是我想做的
如何最好地实现这一点?
5个回答

15

F#也为数组和序列提供了列表推导式。

let records_as_list = 
    [
        while reader.Read() 
            do yield (reader.GetString(0), reader.GetInt64(1)) 
    ]

let records_as_array = 
    [|
        while reader.Read() 
            do yield (reader.GetString(0), reader.GetInt64(1)) 
    |]

let records_as_sequence = 
    seq {
        while reader.Read() 
            do yield (reader.GetString(0), reader.GetInt64(1)) 
    }

F#内置了一个方便的字典函数,称为dict

let records_as_IDictionary = dict records_as_sequence

7
您可以使用序列表达式来实现枚举器:
let records = seq { while reader.NextResult() do yield (reader.GetString(0), reader.GetInt64(1)) }

如果您需要超过两个字段,可以生成列索引(或名称)->字段值的映射(未经测试的代码):
let dbSchema = reader.GetSchemaTable()

let makeMap (rdr : IDataReader) (schema : DataTable) =
    schema.Columns |> Seq.cast<DataColumn> |> Seq.map (fun col -> (col.ColumnName, rdr.[col.ColumnName])) |> Map.ofSeq

let records = seq { while reader.NextResult() do yield (makeMap reader dbSchema) }

seq 的问题在于它采用惰性加载,因此当您想要读取它时,在这种情况下读取器将被关闭。 - mamu
只需在最后一行附加 |> Seq.toList 来实现集合的实例化,问题就很容易解决了。 - Mau

3

对于这种任务,我首先希望将输入转换为一系列字符串。

.Net 4.0提供了ReadLines函数,其返回值类型是seq<string>

   open System.IO
   let readLinesSeq = File.ReadLines

在较早版本的.Net中,需要实现以下函数:
   let readLines filePath = seq {
    use sr = new StreamReader (filePath)
    while not sr.EndOfStream do
     yield sr.ReadLine ()
   }

是的,基本思路是一样的。 - Joel Mueller

2
我可以通过给IDataReader添加扩展属性来更加通用地解决这个问题,这会将任何数据读取器转换为seq<IDataRecord>...
[<AutoOpen>]
module DataReaderEx =
    open System.Data

    type IDataReader with
        member this.toSeq =
            seq { while this.Read() do yield this :> IDataRecord }

这将使您能够在任何数据读取器上使用Seq模块的普通函数:
reader.toSeq
|> Seq.map (fun row -> row.GetString(0), row.GetInt64(1))

0

好的,我们这里不是在谈论文件,然而,在早期的.NET版本中,你可以处理(小文件):

reader.ReadToEnd().Split(System.Environment.NewLine.ToCharArray())

其中reader是一个TextReader。


如果文件很大,则应避免读取所有内容并进行拆分。 - Yin Zhu
在这个问题中,reader实际上是一个IDataReader,而不是TextReader。 - Joel Mueller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接