无点写f#序列表达式

无点写f#序列表达式,f#,pointfree,F#,Pointfree,我正在学习无点写作,事情进展顺利,直到我遇到以下情况: let rec toSeq (reader : SqlDataReader) toItem = seq { if reader.Read() then yield toItem reader yield! toSeq reader toItem else yield! Seq.empty } 这是: let execut

我正在学习无点写作,事情进展顺利,直到我遇到以下情况:

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 execute sql init toItem=
序号{
使用命令=新的SqlCommand(sql)
命令|>init
使用连接=新的SqlConnection(“”)
connection.Open()
命令.连接顺序.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
let ExecuteReader(命令:SqlCommand)(连接:SqlConnection)=
command.Connection>)x)|((>>)toSeq)(使用翻转)
>>|使用
>>|使用

好吧,正如评论中已经指出的,以无点样式编写命令式代码根本不是一个好主意。它不仅不会使代码更具可读性,而且会使代码更容易出错,因为它更难对执行进行推理。即使使用函数式代码,我也经常发现非无点样式的代码更具可读性(不会太久了)

不管怎样,既然你问了,这里有一些你可以采取的步骤-注意,这实际上是一个功能混淆的练习,而不是你想做的事情

第一个函数的代码将被分解为如下内容(这将降低效率,因为序列表达式已优化):

即使是在无点样式中,您仍然需要
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)
您不能以无点方式处理成员调用,因此需要定义调用
Read
的函数。然后还可以使用返回常量函数的函数(为了避免名称冲突,命名为
konst
):

使用这两个参数,可以将最后一个和第二个参数转换为无点样式:

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))
这并不完全是可编译的,因为
toSeq5
是作为其定义的一部分进行计算的,但是如果插入了一些延迟函数,它实际上可能会执行与最初相同的操作

摘要-我不知道上面的代码是否正确,以及它的计算方式(它可能会急切地评估读者,或者包含其他类型的错误)。它进行类型检查,因此可能离工作不远。代码完全不可读,难以调试,无法修改


把这看作是一个极端的例子,你可以用F#编写疯狂的无点代码。在实践中,我认为无点风格应该只用于一些琐碎的事情,比如使用
>
编写函数。一旦你需要定义组合符,比如
uncurry
konst
,人们就很难读懂你的代码de.

无点风格适用于使用输入和输出组合函数,而不是执行命令式语句。我知道。请参阅问题的“仅供参考”部分。@Brad:那么你希望用这段代码做什么?我读到了——这并不是说它“看起来毫无意义”,这是因为它不适用。我不确定,但如果你能让这一点免费,这肯定是一个令人憎恶的问题。太好了!谢谢你。我知道这不是应该在实践中做的事情,它肯定不会最终出现在生产代码中,但有时我的好奇心不允许我忽略一个问题。我无法找出sequ你给了我我想要的东西。
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))
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))
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))