Я только начинаю изучать F#. Я написал этот код F#/ADO.NET вчера вечером. В каких путях Вы улучшились бы, синтаксис - заставляют его чувствовать себя подобно идиоматическому F#?
let cn = new OleDbConnection(cnstr)
let sql = "SELECT * FROM People"
let da = new OleDbDataAdapter(new OleDbCommand(sql, cn))
let ds = new DataSet()
cn.Open()
let i = da.Fill(ds)
let rowCol = ds.Tables.[0].Rows
let rowCount = rowCol.Count
printfn "%A" rowCount
for i in 0 .. (rowCount - 1) do
let row:DataRow = rowCol.[i]
printfn "%A" row.["LastName"]
Примечание: Я нашел, что программе проверки синтаксиса не нравился rowCol. [я]. ["LastName"], Что надлежащий путь состоит в том, чтобы обработать двойные индексаторы? Я должен был разбить код более чем две строки.
Также, Если я не спустился по маршруту DataSet и использовал SqlDataReader, который загрузил его данные в записи F#. Какую структуру набора я должен использовать для содержания записей? Стандартный Список.NET <>?
Ключевая часть вашего кода связана с .NET API, который не работает, поэтому нет способа сделать эту часть кода более идиоматичной или более приятной. Однако ключевым моментом в функциональном программировании является абстракция , поэтому вы можете скрыть этот (уродливый) код в какой-нибудь идиоматической и многократно используемой функции.
Для представления коллекций данных в F # вы можете использовать стандартный тип списка F # (который подходит для функциональной обработки данных) или seq <'a>
(который является стандартным .NET IEnumerable <'a>
под обложкой), который прекрасно работает при работе с другими библиотеками .NET.
В зависимости от того, как вы обращаетесь к базе данных в другом месте кода, может работать следующее:
// Runs the specified query 'sql' and formats rows using function 'f'
let query sql f =
// Return a sequence of values formatted using function 'f'
seq { use cn = new OleDbConnection(cnstr) // will be disposed
let da = new OleDbDataAdapter(new OleDbCommand(sql, cn))
let ds = new DataSet()
cn.Open()
let i = da.Fill(ds)
// Iterate over rows and format each row
let rowCol = ds.Tables.[0].Rows
for i in 0 .. (rowCount - 1) do
yield f (rowCol.[i]) }
Теперь вы можете использовать функцию query
, чтобы написать исходный код примерно так:
let names = query "SELECT * FROM People" (fun row -> row.["LastName"])
printfn "count = %d" (Seq.count names)
for name in names do printfn "%A" name
// Using 'Seq.iter' makes the code maybe nicer
// (but that's a personal preference):
names |> Seq.iter (printfn "%A")
Другой пример, который вы можно написать:
// Using records to store the data
type Person { LastName : string; FirstName : string }
let ppl = query "SELECT * FROM People" (fun row ->
{ FirstName = row.["FirstName"]; LastName = row.["LastName"]; })
let johns = ppl |> Seq.filter (fun p -> p.FirstName = "John")
BTW: Что касается предложения Мау , я бы не стал чрезмерно использовать функции высшего порядка, если есть более прямой способ написания кода с использованием языковых конструкций, таких как для
. Приведенный выше пример с iter
достаточно прост, и некоторые люди сочтут его более читаемым, но общего правила нет ...
Ну, в первом бите мало что можно изменить, но всякий раз, когда вы обрабатываете наборы данных, например, в последних нескольких строках, вы можете использовать встроенные функции Seq, List, Array.
for i in 0 .. (rowCount - 1) do
let row:DataRow = rowCol.[i]
printfn "%A" row.["LastName"]
=
rowCol |> Seq.cast<DataRow>
|> Seq.iter (fun row -> printfn "%A" row.["LastName"])
Я написал функциональную оболочку над ADO.NET для F # . С этой библиотекой ваш пример выглядит так:
let openConn() =
let cn = new OleDbConnection(cnstr)
cn.Open()
cn :> IDbConnection
let query sql = Sql.execReader (Sql.withNewConnection openConn) sql
let people = query "select * from people" |> List.ofDataReader
printfn "%d" people.Length
people |> Seq.iter (fun r -> printfn "%s" (r?LastName).Value)