F#-在给定元组条件的困境中

时间:2020-03-06 15:00:54  来源:igfitidea点击:

给定以下内容:

#light
//any function returning bool * 'a
let foo =
    let x = ref 10
    fun () ->
        x := !x - 1
        if !x <> 0 then
            (true, x)
        else
            (false, x)

while let (c,x) = foo() in c do print_any x;//can't access x, but would be convinent.

//this is how I want it to work, without all the typing
let rec loop f =
    match f() with
    | (true, x) ->
        print_any x
        loop f
    | (false, _) -> ()
loop foo

我应该如何解决这个问题?
还是我应该简单地将" foo"转换为序列表达式?

解决方案

这是一种解决方案,但我个人认为这是对while构造的滥用。

#light
while 
   (let (c,x) = foo()
    if c then print_any !x
    c)
   do ()

我认为另一种解决方案稍微好一点。它使x脱离while的条件子句范围,并将其放入引用y中,该引用在更高范围中可用。仍然不是最好的(功能性)解决方案,但它可以工作。

let y = ref 1
while (let (c,x) = foo()
       y := !x
       c)
       do printf "%i" !y

我认为rec loop解决方案效果最好,因为它是功能最强大的一种(避免副作用,尽管foo使用状态)和最通用的一种(它与foo同时作用于所有功能)。它的输入时间更长,但是如果我们将使用更多的函数(如foo),则循环比仅用foo的最短解决方案更具生产力。

我什至可以泛化一些循环,然后抽象出在"真实"情况下要使用值执行的操作:

let loop f a = 
   let rec loop2() = 
      match f() with
      | (true, x) ->
         a x
         loop2()
      | (false, _) -> ()
   loop2()

loop foo print_any

我喜欢其他假设,假设foo是固定的,如何使用" foo"。

在我看来," foo"的代码闻起来很香。如果合理的话,可以将" foo"转换为" bar"

let bar =    
    let x = ref 10    
    seq {
        x := !x - 1        
        while !x <> 0 do
            yield x
            x := !x - 1        
    }
bar |> Seq.iter print_any

然后我会做,但是" bar"虽然好一点,但看起来仍然很脏。 (在" bar"中,我保留了一个奇怪的方面,即它像" foo"一样返回" int ref"而不是" int",但希望该方面不是故意的吗?)

我认为关于" foo"的事情很奇怪,是隐式信息,这种隐式信息在数据类型中并不明显(只要布尔部分为真,就可以继续调用它),这使seq版本有点更具吸引力。