【问题标题】:Finding largest value using tail recursion使用尾递归找到最大值
【发布时间】:2017-08-16 15:14:04
【问题描述】:

我正在尝试使用尾递归查找列表的最大值。不过,我不能使用任何辅助功能……所以必须使用递归来完成。我写了一个函数来查找最大值,从头部开始,但不知道如何从尾部开始!

lmax []  = error "empty list"
lmax [x] = x
lmax (x::y::xs) = 
  if x > y then lmax (x::xs)
  else          lmax (y::xs)

【问题讨论】:

  • 随着其他与“查找列表中的最大整数”相关的问题弹出,这听起来像是家庭作业。不要使用 Stack Overflow 进行无脑复制粘贴。提出明智的问题。

标签: functional-programming sml smlnj


【解决方案1】:

“尾递归”一词与列表的尾部无关,它与函数调用的位置有关。

你可以说函数调用处于尾部位置,或者它是尾部调用,如果它是函数中发生的最后一件事,即没有其他计算依赖于它。

比较

fun foo xs = List.length xs

fun bar xs = 1 + List.length xs

首先,对List.length 的调用处于尾部位置,因为它的结果会立即返回。
第二,由于我们将长度加 1,所以调用 不是尾调用。

“尾递归”是指递归函数调用是尾调用。

所以你很幸运:你的函数已经是尾递归的,因为两个条件分支都只返回递归调用的值。

【讨论】:

    【解决方案2】:
    fun lmax l = let
      fun lmaxh [] a = a
        | lmaxh (x::xs) a = lmax xs Int.max(x,a)
      in
        lmaxh l 0
      end
    

    这是可行的,假设值是非负整数。

    【讨论】:

    • 我认为使用 int.max 被认为是一个辅助功能
    • 在这种情况下,将其替换为 (if x > a then x else a)。
    【解决方案3】:

    实现尾递归可以优化效率,因为在创建递归调用后不必评估和“弹出”堆栈。

    一般来说,要使用尾递归,您必须从先前的计算中存储一些“内存”以与当前计算进行比较,并为将来的计算更新它,以便在基本情况下立即退出函数。

    因此,您的函数已经尾递归了。

    但是,这里有一个尾递归 maxList 函数,更符合 SML 的精神:

    fun maxList l =
       let 
          fun maxListHelper l acc = 
             case l of 
                 [] => acc
               | x :: xs' => if x > acc 
                             then (maxListHelper xs' x) 
                             else (maxListHelper xs' acc)
       in
         case l of
             [] => error "Empty List!"
           | x :: xs' => maxListHelper xs' x
       end
    

    你的函数是用非常类似Haskell的语法编写的,不同的情况在不同的行上处理,而不是显式地声明为函数定义中的嵌套情况。这很好,但通常不会在 SML 中完成。

    【讨论】:

      猜你喜欢
      • 2019-09-08
      • 2020-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-03
      • 1970-01-01
      • 2021-12-08
      • 2016-06-14
      相关资源
      最近更新 更多