我发现自己偶尔会在作业中编写循环(递归函数)。它会产生这样的尴尬代码:
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)
也许这样更好 - 至少它看起来更像是赋值而不是函数定义。以下是我的问题:
let
中的递归函数是否会产生代码异味?loop
功能可以进一步推广,还是以某种方式改进?答案 0 :(得分:4)
这些问题有点主观,但这是我的答案:
我是这样做的:
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 -> ...)