抽象循环

时间:2011-09-01 20:34:28

标签: recursion f#

我发现自己偶尔会在作业中编写循环(递归函数)。它会产生这样的尴尬代码:

let value =
  let rec loop a =
    if ... then a
    else loop a.B
  loop a

我知道我可以将循环移到let绑定之外,但它的唯一目的是计算绑定值。

所以我想我可能会把循环抽象成一个单独的函数:

let loop f a =
  let rec aux a =
    match f a with
    | Some b -> aux b
    | None -> a
  aux a

然后我可以做:

let value = a |> loop (fun a -> if ... then None else Some a.B)

也许这样更好 - 至少它看起来更像是赋值而不是函数定义。以下是我的问题:

  1. let中的递归函数是否会产生代码异味?
  2. 有没有更好的方法来重构这个?
  3. 如果没有,我的loop功能可以进一步推广,还是以某种方式改进?

3 个答案:

答案 0 :(得分:4)

这些问题有点主观,但这是我的答案:

  1. 没有
  2. 我认为你得到的是好的。
  3. 我是这样做的:

    let rec loop guard step init =
        if guard init then init
        else loop guard step (step init)
    
    let value = a |> loop (fun a -> ...) (fun a -> a.B)
    

答案 1 :(得分:1)

或者你可以定义y-combinator(我从How do I define y-combinator without "let rec"?复制了这个定义)

let rec y f x = f (y f) x

并执行此操作:

let value = a |> y (fun loop a -> if ... then a else loop a.B)

答案 2 :(得分:0)

我认为你或者kvb的解决方案非常好。

我不知道你在循环中迭代的数据结构是什么。它看起来像一个数据结构,因此实现IEnuemerable<'T>接口或编写一个将其转换为IEnumerable<'T>的函数可能是有意义的:

let rec asSeq a = seq {
  yield a 
  yield! asSeq a.B }

然后你可以使用Seq.find并给它你需要的条件:

// Using explicit conversion function
let value = a |> asSeq |> Seq.find (fun a -> ...)

// If 'a' actually implements 'seq<'T>', it is even nicer:
let value = a |> Seq.find (fun a -> ...)
相关问题