Recursion 为什么在F#中使用递归函数而不是'while true do'?

Recursion 为什么在F#中使用递归函数而不是'while true do'?,recursion,f#,functional-programming,Recursion,F#,Functional Programming,在观看Tomas Petricek(我想他知道他在说什么)的视频时,我看到了如下代码 let echo = MailboxProcessor<string>.Start(fun inbox -> async { while do true let! msg = inbox.Receive() printfn "Hello %s" msg }) 从复制的代码 这里的内部函数是递归的,在主函数声明结束之前,通过使用基值调用它

在观看Tomas Petricek(我想他知道他在说什么)的视频时,我看到了如下代码

let echo =
  MailboxProcessor<string>.Start(fun inbox ->
    async {
      while do true
        let! msg = inbox.Receive()
    printfn "Hello %s" msg
    })
从复制的代码

这里的内部函数是递归的,在主函数声明结束之前,通过使用基值调用它来启动

现在我意识到,在第二种情况下,递归是一种将私有值传递给内部函数的简便方法,而不必使用可变的局部值,但是在这里使用递归而不是
,而dotrue
?使用递归编写第一个代码段有什么好处吗


我发现非递归版本更容易阅读(当然是主观意见),这似乎是尽可能使用它的一个很好的理由。

在许多情况下,它取决于您喜欢如何编写代码。就像你的例子一样。 可以编写递归的所有内容也可以使用循环编写,但有时像使用递归数据结构一样,使用递归样式编写更容易。 在大学里,我学到了递归编程,你只需要看看你的下一步,这是非常方便的

您可能对这个问题感兴趣,因为它进一步解释了我的答案:

谈到
邮箱处理器
具体来说,我认为选择取决于你到底在做什么。通常,您可以在循环或递归时始终使用

递归使使用不可变状态变得更容易,如果没有状态或使用可变状态,我发现
while
循环更好。使用可变状态通常非常有用,因为
MailboxProcessor
可以防止并发访问,并且可以将状态保持为本地状态,因此像
Dictionary
(高效哈希表)这样的东西通常很有用

一般而言:

  • 如果您不需要任何状态,我更喜欢
    while
  • 如果您有可变状态(如
    字典
    大小数组
    ),我会选择
  • 如果您有一些不可变的状态(如函数列表或整数),那么递归就更好了
  • 如果您的逻辑在多种操作模式之间切换,那么您可以将其编写为两个相互递归的函数,这对于循环来说是不可行的
在F#
中用于
,而
循环表达式缺少其他语言中常见的功能:

  • 继续
    -跳过循环的其余部分,然后从循环表达式的顶部重新启动
  • 中断
    -过早停止循环
  • 如果您想要
    continue
    break
    请不要像我刚开始做的那样,为
    while
    循环编写一个非常复杂的测试表达式。相反,尾部递归是F#中的最佳答案:

    有时,当您在寻找性能时,一个技巧是循环到
    0
    (保存CPU寄存器)。不幸的是,F#为
    循环代码生成
    的方式没有人们希望的那么好:

    for i = 100 downto 0 do
      printfn "Unfortunately this is not as efficient as it can be"
    
    朝向
    0的尾部递归保存CPU寄存器


    (不幸的是,F#编译器没有为尾部递归合并测试和循环指令,因此它没有达到预期的效果)

    谢谢,回复很好,链接也很有趣。我会接受托马斯的回答,因为他把选项说得更清楚了一点,但我也很感激你的回答。谢谢,这些要点说得很清楚。也谢谢你的精彩课程,请多做一些!
    let vs : int [] = ...
    let rec findPositiveNumberIndex i =
      if i < vs.Length then
        if vs.[i] > 0 then 
          Some i
        else 
          findPositiveNumberIndex (i + 1)
      else
        None
    match findPositiveNumberIndex 0 with
    | Some i -> printfn "First positive number index: %d" i
    | None   -> printfn "No positive numbers found"
    
    for i in 0..100 do
      printfn "This is a fast loop"
    
    for i in 0..2..100 do
      printfn "This is a slow loop"  
    
    for i in 0L..100L do
      printfn "This is a slow loop"    
    
    for i = 100 downto 0 do
      printfn "Unfortunately this is not as efficient as it can be"