SML中的尾递归不提供任何输出 [英] Tail recursion in SML does not present any output

查看:196
本文介绍了SML中的尾递归不提供任何输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继我的上一篇文章此处 ,我试着去做所建议的事情,并将代码
转换为一个Tail-recursion方法,其中 let



原始代码不起作用(由于在内使用 val > if 条件):

  fun func()= 

val decimal = 0(*最终结果*)
val multiple = 0(*记录倍数,例如在XXV中,X将是倍数*)
val当前值= 0(*当前正在处理的数字*)
val top = 0(*列表中最后一个元素的值*)
val last_add = 0(*不是多重或减法运算*的最后一位数字)
val last_sub = 0
val问题= 0(*如果值为1,那么输入存在问题*)
val myList = [1,2,3,4,5](*列表中有更多值*)

while(myLis t< [*] $ * b
$ b val current = tl(myList)(*获取列表中的最后一个元素*)
val myList = tl(myList )(*删除列表中的最后一个元素*)
val top = tl(myList)(*获取列表末尾的值*)
if(myList<> []) andalso(current> top))
then

val decimal = decimal + current - top
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 =当前

而代码作为尾部 - 递归方法:

  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问题= 0
val doNothing = 0
in

let
val current = hd(rev(head :: tail))(*获取最后一个元素*)
val head :: tail = rev tl(rev(head :: tail)))(* POP action - 从列表中删除最后一个元素*)
val top = hd(rev(head :: tail))(*在
中删除*)
if(current>然后

val decimal = decimal + current - top
val last_sub = top
val head :: tail = rev(tl(rev(head :: tail)) )(* POP action - 从列表中删除最后一个元素*)
in
calc(head :: tail)
end
else
if((head: :tail = [])andalso(current = top))
然后让
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 =当前

calc(head :: tail)
结束
else
(*什么都不做*)
val没有= 0
结束

结束;

然而,当我尝试输入时:

 计算值([0,100,20,30,4,50]); 

我得到:

 未捕获的异常Bind [非穷举性绑定失败] 
在:stdIn引发:216.13-216.50

我知道这段代码很难阅读并且很长,但是如果有人能够向我解释如何解决这个问题,或者帮我找到这个输出的原因,我会非常感激
。 / p>

谢谢 解决方案

您的代码存在一些问题。 / p>

首先,您可以使用 last 来获取列表的最后一个元素。有关更多信息,请参见列表文档。但是,除非你有充分的理由这样做,否则从列表开始处简单地开始并在开始时将元素从头开始放置会更容易和更有效。你已经有第一个元素绑定到使用模式匹配的代码中 head

其次,除非你使用 ref s(你可能不想这样做),Standard ML中只有值没有变量。这意味着如果你想在调用之间进行状态转移,任何累加器都需要成为你的函数的参数。使用辅助函数来初始化累加器是一种常见的模式。

第三,不是比较列表与 [] 要测试它是否为空,请使用 null 函数。请相信我。由于微妙的类型推断问题,您将使用 = 获取警告。更好的是,在函数的参数上使用模式匹配,或使用 case 语句。模式匹配允许编译器告诉你是否处理了所有可能的情况。



第四,SML通常对变量名使用camelCase而不是snake_case。这是更具风格的,但当你编写更多的代码和协作,你会想要符合约定。第五,当你在列表上进行递归时,不要试图查看列表中的多个值。这使事情变得复杂。把它当作头元素和尾单,一切都会变得简单多了。在我的代码中,我没有将列表保存在当前列表中,而是通过将其拆分为单独的参数来完成此操作。有一个简单的例子,您只需从一个累加器返回答案,然后使用一个递归的情况下递归更新的累加器值和从列表中弹出的单个值。这消除了这个问题。



我不确定这个逻辑是否正确,因为我不知道你在计算什么,但看看这段代码它说明了我谈到的一些事情。

 (*这是辅助函数,它将累加器作为
参数。*)
fun calc'decimal _ _ _ _ [] =
(*我们处理了列表中的所有内容,只返回累加器。*)
十进制
| calc'decimal multiple lastAdd lastSub current(top :: tail)=
(*这种情况适用于列表中有一个或多个元素的情况*)
if current>然后
calc'(decimal + current - top)multiple lastAdd top top tail
else if if current = top then
calc'(decimal + current)(multiple + 1)lastAdd lastSub top tail
else
calc'(decimal + current)0 current lastSub top tail

(*这是您应该调用的函数*)
fun calc [] = 0
| calc [_] = 0(*给定一个单元素列表。*)
| calc(x :: xs)=
(*以正确的初始值应用助手*)
calc'0 0 0 0 x xs

在函数式语言中,当您想要更改变量时,不必为变量指定变量,只需简单地为正确的参数递归并指定新值即可。这就是你如何使用递归在函数式语言中编写一个循环。只要您只使用尾递归,它就会像您最喜欢的命令式语言中的while循环一样高效。


Following my previous post here , I tried to do what was suggested and convert the code into a Tail-recursion method with let .

The original code - which does not work (due to using val inside if condition) :

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. 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.

Secondly, unless you use refs (which you probably don't want to do) there are no variables in Standard ML, only values. 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. 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. 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. 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.

这篇关于SML中的尾递归不提供任何输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆