F# append to list in a loop functiuonally

F# append to list in a loop functiuonally

我希望将此代码转换为使用 F# 列表而不是 C# 列表实现。

我正在连接到一个数据库,运行 通常使用 C# 的查询会创建一个类型的列表,并在数据读取器具有值时继续添加该列表。我将如何将其转换为使用 F# list

    let queryDatabase (connection: NpgsqlConnection) (queryString: string) =
        let transactions = new List<string>()
        let command = new NpgsqlCommand(queryString, connection)
        let dataReader = command.ExecuteReader()

        while dataReader.Read() do
           let json = dataReader.GetString(1)
           transactions.Add(json)
        transactions

这里的棘手之处在于输入数据源本质上是命令式的(您必须调用 Read 来改变内部状态)。因此,您正在从命令式世界过渡到功能性世界 - 因此您无法避免所有变化。

我可能会使用列表理解来编写代码,它保持类似的熟悉结构,但删除了显式突变:

let queryDatabase (connection: NpgsqlConnection) (queryString: string) =
  [ let command = new NpgsqlCommand(queryString, connection)
    let dataReader = command.ExecuteReader()
    while dataReader.Read() do
      yield dataReader.GetString(1) ]

Tomas 的回答是在产品代码中使用的解决方案。但是为了学习 F# 和函数式编程,我用 tail recursion 和 cons operator:

展示了我的代码片段
let drToList (dr:DataReader) =
    let rec toList acc =
        if not dr.Read then acc
        else toList <| dr.GetString(1) :: acc
    toList []

这个tail递归函数被编译成命令式代码,因此没有堆栈溢出和快速执行。

另外,我建议您查看 this C# thread and this F# 文档,了解如何正确处理您的命令。基本上,您需要像这样使用 smth:

let queryDb (conn: NpgsqlConnection) (qStr: string) =
   use cmd = new NpgsqlCommand(qStr, conn)
   cmd.ExecuteReader() |> drToList

如果我们更深入,您还应该考虑异常处理。