好吧,正如评论中已经指出的那样,以无点风格编写命令式代码根本不是一个好主意。它不仅不会使它更具可读性,而且会使其更容易出错,因为它更难以推理执行。即使使用函数式代码,我也经常发现非无点样式更具可读性(而且不会更长)。
无论如何,既然你问了,这里有一些你可以采取的步骤 - 请注意,这实际上是一个功能混淆练习。不是你想做的事。
第一个函数的代码将被简化为类似这样的代码(这会降低效率,因为序列表达式已经过优化):
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
作为其定义的一部分进行评估,但是如果您插入一些延迟函数,它实际上可能会做与最初所做的相同的事情。
总结- 我不知道上面的代码是否正确以及它如何评估(它可能会急切地评估读者,或者包含其他类型的错误)。它会进行类型检查,因此它可能离工作不远。代码完全不可读,难以调试且无法修改。
Think of this as an extreme example of crazy point-free code you can write in F#. In practice, I think that point-free style should only be used for trivial things like composing functions using >>
. Once you need to define combinators like uncurry
or konst
, it will be really hard for people to read your code.