简体   繁体   English

SML中的尾递归不会显示任何输出

[英]Tail recursion in SML does not present any output

Following my previous post here , I tried to do what was suggested and convert the code into a Tail-recursion method with let . 继我以前的帖子在这里 ,我试图做什么建议和代码转换成一个尾递归方法let

The original code - which does not work (due to using val inside if condition) : 原代码- 这不工作 (由于使用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

And the code as a tail-recursion method : 并将代码作为尾递归方法:

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; 

However , when I try to enter : 但是,当我尝试输入时:

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

I get : 我明白了:

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

I know the code is very hard to read and pretty long , but it would be greatly appreciated if someone could explain to me how to fix it , or help me find the reason for this output . 我知道代码很难阅读并且很长,但如果有人能向我解释如何修复它,或者帮助我找到输出的原因,我将不胜感激。

Thanks 谢谢

You have a few issues with your code. 您的代码存在一些问题。

First of all, you can use last to grab the last element of a list. 首先,您可以使用last来获取列表的最后一个元素。 See the List documentation for more info. 有关详细信息,请参阅列表文档 But unless you have a really good reason to do so, it's easier and much more efficient to simply start from the beginning of the list and pop elements off the beginning as you recurse. 但除非你有充分的理由这样做,否则从列表的开头简单地开始并且在你开始时从头开始弹出元素会更容易,也更有效率。 You already have the first element bound to head in your code using pattern matching. 您已经绑定到第一个元素head使用模式匹配在你的代码。

Secondly, unless you use ref s (which you probably don't want to do) there are no variables in Standard ML, only values. 其次,除非你使用ref (你可能不想这样做),标准ML中没有变量,只有值。 What this means is that if you want to carry state between invocations, any accumulators need to be parameters of your function. 这意味着如果你想在调用之间携带状态,任何累加器都需要是你函数的参数。 Using a helper function to initialize accumulators is a common pattern. 使用辅助函数初始化累加器是一种常见的模式。

Third, instead of comparing a list to [] to test if it's empty, use the null function. 第三,不是将列表与[]进行比较来测试它是否为空,而是使用null函数。 Trust me on this. 相信我。 You'll get warnings using = because of subtle type inference issues. 由于微妙的类型推断问题,您将收到使用=警告。 Better yet, use a pattern match on your function's parameters or use a case statement. 更好的是,在函数的参数上使用模式匹配或使用case语句。 Pattern matching allows the compiler to tell you whether you've handled all possible cases. 模式匹配允许编译器告诉您是否已处理所有可能的情况。

Fourth, SML typically uses camelCase, not snake_case, for variable names. 第四,SML通常使用camelCase而不是snake_case来表示变量名。 This is more stylistic, but as you write more code and collaborate, you're going to want to fit with the conventions. 这更具风格,但是当您编写更多代码并进行协作时,您将希望符合这些约定。

Fifth, when you do recursion on a list, don't try to look at multiple values in the list. 第五,当你在列表上进行递归时,不要试图查看列表中的多个值。 This complicates things. 这使事情复杂化。 Treat it as a head element and tail list, and everything will become much simpler. 将它视为头部元素和尾部列表,一切都将变得更加简单。 In my code, instead of keeping current in the list, I did this by splitting it out into a separate parameter. 在我的代码中,我没有在列表中保持当前状态,而是将其拆分为单独的参数。 Have a base case where you simply return the answer from one of your accumulators, and a recursive case where you recurse with updated accumulator values and a single value popped from your list. 有一个基本情况,你只需要从你的一个累加器中返回答案,以及一个递归的情况,你可以用更新的累加器值和从列表中弹出的单个值进行递归。 This eliminates the problem scenario. 这消除了问题场景。

I'm not sure if this logic is correct since I don't know what you're trying to calculate, but check out this code which illustrates some of the things I talked about. 我不确定这个逻辑是否正确,因为我不知道你想要计算什么,但查看这段代码说明了我谈到的一些事情。

(* 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

In a functional language, instead of assigning to a variable when you want to change it, simply recurse and specify the new value for the correct parameter. 在函数式语言中,只需递归并指定正确参数的新值,而不是在想要更改它时分配给变量。 This is how you write a "loop" in a functional language using recursion. 这就是你使用递归在函数式语言中编写“循环”的方法。 As long as you only use tail-recursion, it will be just as efficient as a while loop in your favorite imperative language. 只要您只使用尾递归,它就会像您最喜欢的命令式语言中的while循环一样高效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM