递归调用。如何以及为什么要返回最大值? (新泽西州的标准ML)

时间:2018-08-11 10:27:52

标签: sml smlnj

这是可怕的代码,但它可以工作。我只是想知道这个递归调用如何分辨最大/最大的数字?尝试解释一下就好像我7岁:)

fun badmax (xs : int list)=
    if null xs
    then 0
    else if null (tl xs)
    then hd xs
    else if hd xs > badmax(tl xs)
    then hd xs
    else badmax(tl xs)

2 个答案:

答案 0 :(得分:2)

首先阅读nulltlhd。一些提示可以帮助您入门。

fun badmax (xs : int list)=
    if null xs  <- If the list is empty, return 0
    then 0
    else if null (tl xs)  <- If there is nothing returned by `null (tl xs)`, 
                             we will return hd xs.
                             Q: Are you sure there is anything left in `hd xs`? 
                             Note that if there is nothing left, calling
                             `hd xs` will raise exception. How can you be sure? 
    then hd xs
    else if hd xs > badmax(tl xs) Q: What are we comparing here? 
                                     Think about what does `badmax(tl xs)` 
                                     return and why would we return `hd xs` 
                                     if the condition is satisfied.
                                  Again, ask why `hd xs` and `tl xs` are legal.
    then hd xs
    else badmax(tl xs)            Q: Why would we want to return `badmax(tl xs)`

答案 1 :(得分:1)

空白列表中最大的元素不是0。

您可以使用例外处理空列表的特殊情况:

fun max [] = raise Empty
  | max [x] = x
  | max (x::xs) = case max xs of
                    y => if x > y then x else y

您可以通过一次替换一个表达式来运行它(如数学运算):

max [3,9,7]
  ~> case max [9,7] of y1 => if 3 > y1 then 3 else y1
  ~> case (case max [7] of y2 => if 9 > y2 then 9 else y2) of
       y1 => if 3 > y1 then 3 else y1
  ~> case (case 7 of y2 => if 9 > y2 then 9 else y2) of
       y1 => if 3 > y1 then 3 else y1
  ~> case (if 9 > 7 then 9 else 7) of
       y1 => if 3 > y1 then 3 else y1
  ~> case 9 of y1 => if 3 > y1 then 3 else y1
  ~> if 3 > 9 then 3 else 9
  ~> 9

由于 case-of ,中间结果看起来很复杂。您可以使用 let-in-end 以类似的方式定义函数,但是中间结果可能同样令人困惑。

fun max [] = raise Empty
  | max [x] = x
  | max (x::xs) = let val y = max xs
                  in if x > y then x else y
                  end

有一个名为Int.max的库函数,该函数接受两个整数的元组并返回较大的整数。它使代码和中间结果看起来更加整洁:

fun max [] = raise Empty
  | max [x] = x
  | max (x::xs) = Int.max (x, max xs)

您可以“手动”运行该功能以查看其工作原理:

max [3,9,7,5,6]
  ~> Int.max (3, max [9,7,5,6])
  ~> Int.max (3, Int.max (9, max [7,5,6]))
  ~> Int.max (3, Int.max (9, Int.max (7, max [5,6])))
  ~> Int.max (3, Int.max (9, Int.max (7, Int.max (5, max [6]))))
  ~> Int.max (3, Int.max (9, Int.max (7, Int.max (5, 6))))
  ~> Int.max (3, Int.max (9, Int.max (7, 6)))
  ~> Int.max (3, Int.max (9, 7))
  ~> Int.max (3, 9)
  ~> 9

仍然,列表越长,调用栈越长。

因此您可以编写一个尾递归版本:

(* Keeps the current largest integer in an extra argument, y *)
fun maxhelp ([], y) = y
  | maxhelp (x::xs, y) = maxhelp (xs, Int.max (x, y))

fun max [] = raise Empty
  | max (x::xs) = maxhelp (xs, x)

当您手动运行相同的输入内容时:

max [3,9,7,5,6]
  ~> maxhelp ([9,7,5,6], 3)
  ~> maxhelp ([7,5,6], Int.max (9, 3))
  ~> maxhelp ([7,5,6], 9)
  ~> maxhelp ([5,6], Int.max (7, 9))
  ~> maxhelp ([5,6], 9)
  ~> maxhelp ([6], Int.max (5, 9))
  ~> maxhelp ([6], 9)
  ~> maxhelp ([], Int.max (6, 9))
  ~> maxhelp ([], 9)
  ~> 9

在第一个版本中,以数字作为一个操作数调用Int.max,并以另一个作为递归调用,并且在Int.max中的任何一个可以调用之前,递归调用依赖于另一个递归调用。得到解决。

在第二个版本中,用两个数字调用Int.max并将结果存储在maxhelp的参数中。因此,当maxhelp进行递归调用时,它取决于Int.max被解析,而不是相反。这很整洁,因为Int.max不是递归的。