Functional programming 使用尾部递归查找最大值

Functional programming 使用尾部递归查找最大值,functional-programming,sml,smlnj,Functional Programming,Sml,Smlnj,我正在尝试使用尾部递归查找列表的最大值。我不能使用任何辅助函数,所以必须使用递归。我写了一个函数,从头部开始查找最大值,但不知道如何从尾部开始实现它 lmax [] = error "empty list" lmax [x] = x lmax (x::y::xs) = if x > y then lmax (x::xs) else lmax (y::xs) 假设值是非负整数,这是可行的。术语“尾部递归”与列表的尾部无关,它与函数调用的位置有关 您可以说函数调

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

lmax []  = error "empty list"
lmax [x] = x
lmax (x::y::xs) = 
  if x > y then lmax (x::xs)
  else          lmax (y::xs)
假设值是非负整数,这是可行的。

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

您可以说函数调用处于尾部位置,或者如果它是函数中最后发生的事情,也就是说,没有其他计算依赖于它,那么它就是尾部调用

比较

fun foo xs = List.length xs

在第一种情况下,对
List.length
的调用处于尾部位置,因为它的结果会立即返回。
在第二种情况下,由于我们在长度上加了1,所以调用不是尾部调用

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


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

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

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

因此,您的函数已经是尾部递归的

但是,这里有一个尾部递归的
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中完成。

我认为使用int.max被认为是一个辅助函数。在这种情况下,将其替换为(如果x>a,那么x else a)。随着其他与“在列表中查找最大整数”相关的问题的出现,这就好像是一个家庭作业。不要使用堆栈溢出进行无脑复制粘贴。提出明智的问题。
fun bar xs = 1 + List.length xs
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