在SML尾递归不存在任何输出(Tail recursion in SML does not pres

2019-11-01 08:15发布

继我以前的帖子在这里 ,我试图做什么建议和代码转换成一个尾递归方法let

原代码- 这不工作 (由于使用valif条件):

fun func() = 

val decimal = 0 (* the final result *)
val multiple = 0 (* keeps track of multiples, eg. In XXV, X would be a multiple *)
val current = 0 (* the digit currently being processed *)
val top = 0   (* value of the last element in the list *)
val last_add = 0 (* the last digit that wasn't a multiple, or subtraction operation *)
val last_sub = 0
val problem = 0 (* if value is 1 then there is a problem with the input *)
val myList = [1,2,3,4,5] (* the list has more values *)

while (myList <> [])    (* run while the list is not empty *)

    val current = tl(myList) (* grab the last element from the list *)
    val myList = tl(myList) (* remove the last element from the list *)
    val top = tl(myList) (* grab the value at the end of the list *)
    if ( myList <> []) andalso (current > top))
       then      

                val decimal = decimal + current - top
                val last_sub = top;
                val myList = tl(myList)
       else     
           if ( (myList = []) andalso (current = top))
              then val decimal = decimal + current
                   val multiple = multiple + 1
              else
                  if (last_sub = current)
                     then val problem = 1

                     else
                          val decimal = decimal + current
                          val multiple = 0
                          val last_add = current

和代码作为尾递归方法:

fun calc [] = 0
    |calc [x] = x
    |calc (head::tail) = 
       let 
          val decimal = 0
          val multiple = 0
          val current = 0
          val top = 0  
          val last_add = 0
          val last_sub = 0
          val problem = 0  
          val doNothing = 0
       in      

          let
              val current = hd(rev(head::tail))  (* grab the last element *) 
              val head::tail = rev(tl(rev(head::tail)))  (* POP action - remove the last element from the list *)
              val top = hd(rev(head::tail))      (* grab the new last element after removing *)
              in 
                if (current > top) then 
                    let 
                          val decimal = decimal + current - top
                          val last_sub = top 
                          val head::tail = rev(tl(rev(head::tail)))  (* POP action - remove the last element from the list *)
                    in
                    calc(head::tail)
                    end
                else
                 if ( (head::tail = []) andalso (current = top))
                   then let 
                          val decimal = decimal + current
                          val multiple = multiple + 1
                        in 
                          calc(head::tail)
                        end
                 else 
                     if (last_sub <> current)
                       then let 
                               val decimal = decimal + current
                               val multiple = 0
                               val last_add = current
                            in 
                               calc(head::tail)
                            end
                     else
                        (* do nothing *)    
                        val doNothing = 0
               end      

       end; 

然而,当我试着输入:

calc([0,100,20,30,4,50]);

我得到:

uncaught exception Bind [nonexhaustive binding failure]
  raised at: stdIn:216.13-216.50

我知道代码是非常难以阅读和很长,但将不胜感激,如果有人能向我解释如何解决它,或者帮助我找到这个输出的原因。

谢谢

Answer 1:

你有你的代码的几个问题。

首先,你可以使用last抢列表的最后一个元素。 请参阅列表文档获取更多信息。 但是,除非你有一个很好的理由这样做,它更容易和更高效简单地从列表的开头开始和流行元素关开始,因为你递归。 您已经绑定到第一个元素head使用模式匹配在你的代码。

其次,除非你使用ref S(你可能不希望这样做),有标准的ML没有变量,只有值。 这意味着,如果你想调用之间携带状态,任何蓄电池必须是你的函数的参数。 使用辅助函数初始化蓄电池是一种常见的模式。

三,而不是一个列表进行比较,以[]以测试它是否是空的,使用null函数。 相信我这一点。 你会使用得到警告=因为微妙的类型推断的问题。 更重要的是,使用你的函数的参数的模式匹配或使用case说明。 模式匹配,编译器可以告诉您是否已经处理所有可能的情况。

四,SML通常使用驼峰,不snake_case,变量名。 这是更文体,但你写更多的代码和协作,你会想,正好与约定。

第五,当你的清单上做递归,不要尝试看看列表中的多个值。 这种复杂的事情。 把它作为一个头元素和尾单,一切都会变得简单多了。 在我的代码,而不是保持当前列表中,我通过拆分出来成为一个单独的参数做了这一点。 有一个基本情况,你只需从你的蓄电池的一个返回答案,并在您使用更新累加器值,并从列表中弹出一个值递归递归情况。 这消除了该问题的方案。

我不知道,如果这逻辑是正确的,因为我不知道你想计算一下,但看看这个代码说明了一些我谈的事情。

(* This is the helper function which takes accumulators as
   parameters. You shouldn't call this directly. *)
fun calc' decimal _ _ _ _ [] =
    (* We processed everything in the list.  Just return the accumulator. *)
    decimal
  | calc' decimal multiple lastAdd lastSub current (top::tail) =
    (* This case is for when there are 1 or more elements in the list. *)
    if current > top then
        calc' (decimal + current - top) multiple lastAdd top top tail
    else if current = top then
        calc' (decimal + current) (multiple + 1) lastAdd lastSub top tail
    else
        calc' (decimal + current) 0 current lastSub top tail

(* This is the function you should call. *)
fun calc [] = 0
  | calc [_] = 0 (* Given a single-element list. *)
  | calc (x::xs) =
    (* Apply the helper with correct initial values. *)
    calc' 0 0 0 0 x xs

在功能性语言,而不是分配给一个变量,当你想改变它,只需重复并指定正确的参数的新值。 这是你如何写在使用递归函数式语言一个“循环”。 只要您只使用尾递归,这将是就像你最喜欢命令式语言while循环,高效。



文章来源: Tail recursion in SML does not present any output