0

在我之前的帖子之后我尝试按照建议进行操作,并将代码转换为带有let.

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

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

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

谢谢

4

1 回答 1

2

您的代码有一些问题。

首先,您可以使用last抓取列表的最后一个元素。有关详细信息,请参阅列表文档。但是除非你有一个非常好的理由这样做,否则简单地从列表的开头开始并在递归时从开头弹出元素会更容易和更有效。您已经head使用模式匹配在代码中绑定了第一个元素。

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

第三,不要比较一个列表来[]测试它是否为空,而是使用该null函数。相信我。=由于微妙的类型推断问题,您会收到警告。更好的是,对函数的参数使用模式匹配或使用case语句。模式匹配允许编译器告诉你是否已经处理了所有可能的情况。

第四,SML 通常使用 camelCase,而不是 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 循环一样高效。

于 2012-12-29T23:46:50.497 回答