我正在学习写点免费,事情变得很好,直到遇到这个:
let rec toSeq (reader : SqlDataReader) toItem = seq {
if reader.Read()
then
yield toItem reader
yield! toSeq reader toItem
else
yield! Seq.empty }
和此:
let execute sql init toItem =
seq {
use command = new SqlCommand(sql)
command |> init
use connection = new SqlConnection("")
connection.Open()
command.Connection <- connection
use reader = command.ExecuteReader()
yield! toSeq reader toItem } |> Seq.toList
我不知道如何通过序列构建器......这甚至可能吗?
我需要确保使用仍然正常。
仅供参考:我知道在这里使用免费点编程似乎毫无意义。请理解这对我来说是一次学习练习。
更新:这是我第一次尝试第二个功能。我不得不删除序列引用:
let ExecuteReader (command : SqlCommand) (connection : SqlConnection) =
command.Connection <- connection
command.ExecuteReader()
let c x y = ((>>) x) << ((<<) << y)
let (>>|) = c
let execute =
ExecuteReader
>>| ((>>) toSeq) (flip using)
>>| using
>>| using
答案 0 :(得分:2)
好吧,正如评论中已经提到的那样,以无点样式编写命令式代码根本不是一个好主意。它不仅不会使它更具可读性,而且它使更容易出错,因为它更难以推断执行。即使使用功能代码,我也经常发现非无点风格的可读性更高(而且时间更长)。
无论如何,既然你问过,这里有一些你可以采取的步骤 - 请注意,这实际上是功能混淆的练习。不是你想做的事。
第一个函数的代码将被这样的东西所取代(这将是效率低下因为序列表达式被优化):
let rec toSeq (reader : SqlDataReader) toItem = Seq.delay (fun () ->
if reader.Read() then
Seq.concat [ Seq.singleton (toItem reader); toSeq reader toItem ]
else
Seq.empty)
即使在无点样式中,您仍然需要Seq.delay
以确保您懒惰地执行序列。但是,您可以定义稍微不同的函数,以便以更无点的方式编写代码。
// Returns a delayed sequence generated by passing inputs to 'f'
let delayArgs f args = Seq.delay (fun () -> f args)
let rec toSeq2 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (fun (reader, toItem) ->
if reader.Read() then
Seq.concat [ Seq.singleton (toItem reader); toSeq2 (reader, toItem) ]
else
Seq.empty)
现在,函数的主体只是传递给delayArgs
函数的一些函数。我们可以尝试以无点样式从其他函数中编写此函数。 if
虽然很棘手,所以我们用一个带有三个函数的组合器替换它(并将相同的输入传递给所有函数):
let cond c t f inp = if c inp then t inp else f inp
let rec toSeq3 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (cond (fun (reader, _) -> reader.Read())
(fun (reader, toItem) ->
Seq.concat [ Seq.singleton (toItem reader);
toSeq3 (reader, toItem) ])
(fun _ -> Seq.empty))
您无法以无点样式处理成员调用,因此您需要定义调用Read
的函数。然后你也可以使用一个返回常量函数的函数(以避免名称冲突,名为konst
):
let read (reader:SqlDataReader) = reader.Read()
let konst v _ = v
使用这两个,您可以将最后一个和第二个参数转换为无点样式:
let rec toSeq4 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (cond (fst >> read)
(fun (reader, toItem) ->
Seq.concat [ Seq.singleton (toItem reader);
toSeq4 (reader, toItem) ])
(konst Seq.empty))
使用一些更疯狂的组合器(uncurry
存在于Haskell中; list2
组合子也可以用无点样式编写,但我认为你明白了这一点:
let list2 f g inp = List.Cons(f inp, List.Cons(g inp, []))
let uncurry f (a, b) = f a b
let rec toSeq5 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (cond (fst >> read)
(list2 ((uncurry (|>)) >> Seq.singleton) toSeq5 >> Seq.concat)
(konst Seq.empty))
这不完全编译,因为toSeq5
被评估为其定义的一部分,但是如果你加入了一些延迟函数,它实际上可能与它原来做的一样。
摘要 - 我不知道上面的代码是否正确及其评估方式(它可能会急切地评估读者,或者包含其他类型的错误)。它进行类型检查,因此它可能离工作不太远。代码完全不可读,难以调试且无法修改。
将此视为您可以用F#编写的疯狂无点代码的极端示例。在实践中,我认为无点样式应仅用于使用>>
组合函数等微不足道的事情。一旦您需要定义uncurry
或konst
等组合器,人们就很难阅读您的代码。