按照我之前发布的帖子here,我尝试按照建议进行操作并转换代码
使用let
进入Tail-recursion方法。
原始代码 - 不起作用(由于在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
我知道代码很难阅读并且很长,但我们将非常感激 如果有人能向我解释如何修复它,或者帮我找到输出的原因。
谢谢
答案 0 :(得分:2)
您的代码存在一些问题。
首先,您可以使用last
来获取列表的最后一个元素。有关详细信息,请参阅List documentation。但除非你有充分的理由这样做,否则从列表的开头简单地开始并且在你开始时从头开始弹出元素会更容易,也更有效率。您已经使用模式匹配在代码中绑定了head
的第一个元素。
其次,除非您使用ref
(您可能不想这样做),否则标准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循环一样高效。