请引导我完成这个“Erlang Programming”递归样本

时间:2009-06-28 16:06:44

标签: recursion erlang

来自Cesarini和Thomson的Erlang Programming的第90页,有一个例子没有详细讨论。我是函数式编程和递归思维的新手,所以我不熟悉以这种方式解决问题。

“例如,以下函数通过交错合并两个列表(长度相同) 他们的价值观:“

merge(Xs,Ys) -> lists:reverse(mergeL(Xs,Ys,[])).

mergeL([X|Xs],Ys,Zs) ->  mergeR(Xs,Ys,[X|Zs]);
mergeL([],[],Zs) ->  Zs.

mergeR(Xs,[Y|Ys],Zs) ->  mergeL(Xs,Ys,[Y|Zs]);
mergeR([],[],Zs) ->  Zs.

这是如何工作的?谢谢!

4 个答案:

答案 0 :(得分:7)

逐步完成

merge([1,2],[3,4])
reverse(mergeL([1,2],[3,4],[]))
reverse(mergeR([2],[3,4],[1]))
reverse(mergeL([2],[4],[3,1]))
reverse(mergeR([], [4], [2,3,1]))
reverse(mergeL([], [], [4,2,3,1]))
reverse([4,2,3,1])
[1,3,2,4]

将这些功能手工处理在一张纸上,并在您尝试计算它的小输入上总是很好的。你很快就会看到它是如何运作的。

答案 1 :(得分:4)

首先调用此函数:

merge(Xs,Ys) -> lists:reverse(mergeL(Xs,Ys,[])).

传递给mergeL的空列表[]是累加器 - 这是答案的来源。请注意,第一个函数调用mergeL - 左合并。

让我们假装这样的函数被调用:

merge([1, 2, 3], [a, b, c])

两个长度相同的列表。然后,第一个函数调用mergeL:

mergeL([X|Xs],Ys,Zs) ->  mergeR(Xs,Ys,[X|Zs]);
mergeL([],[],Zs) ->  Zs.

左合并中有2个子句。使用参数调用mergeL将以自上而下的顺序匹配这些子句。

这些子句中的第二个有三个参数 - 前两个是空列表[]。但是,第一次调用mergeL时,这两个列表不为空,它们是列表X和Y,因此第一个子句匹配。

让我们打破比赛。这是对mergeL的调用:

mergeL([1,2,3],[a,b,c],[])

并以下列方式匹配第一个子句:

X = 1
Xs = [2, 3]
Ys = [a, b, c]
Zs = []

这是因为列表的特殊形式:

[X | Xs]

这意味着将X与列表的头部(单个项目)匹配,并使Xs成为列表的尾部(列表)。

然后我们建立新的函数调用。我们可以将值X添加到列表Zs的开头,就像我们模式匹配它一样,所以我们得到第一个mergeR调用:

mergeR([2,3],[a,b,c],[1])

最后一个参数是一个单项目列表,它是在空列表的头部添加一个项目。

这将持续到最后。

实际上mergeL的最后一个子句是多余的。根据定义,这个函数将在mergeR的最后一个条款中耗尽(但我将把它作为读者的练习)。

答案 2 :(得分:0)

该示例的作用是定义递归将经历的一些状态。定义了3个“函数”: 合并,合并和合并。

要合并的列表是X和Y,而Zs是合并的结果。

合并将从调用'merge'并提供两个列表开始。第一步是调用mergeL和要合并的两个列表,以及一个空的结果集。

[X | Xs]获取列表的第一个元素(非常像array_shift)。此元素将添加到结果集的头部([X | Zs]执行此操作)。然后将此结果集(现在包含一个元素)传递给下一个调用mergeR。 mergeR做同样的事情,只需要从第二个列表中获取一个元素。只要提供给mergeL或mergeR的列表不为空,此行为将继续。

当使用两个空列表([])和结果集(Zs)调用mergeL或mergeR时,它将返回结果集(而不是执行另一次运行,从而停止递归)。

要点:

递归的 start 是第一行,它定义了'merge'。这个开始将通过调用第一个mergeL来设置整个事物。

递归的 body 是第2行和第4行,它们定义了行为,或者mergeL和mergeR,它们互相调用。

递归的停止由第3行和第5行定义,这基本上告诉了当数组中没有更多元素时要做什么。

希望这有帮助!

答案 3 :(得分:0)

我总是首先寻找那些将终止递归的函数,在这种情况下:

mergeL([],[],Zs) ->  Zs.

mergeR([],[],Zs) ->  Zs.
当前两个参数都是空列表时,这两个基本完成“合并”。

然后我看一下函数的第一次调用:

merge(Xs,Ys) -> lists:reverse(mergeL(Xs,Ys,[])).

忽略反向一秒钟,您将看到最后一个参数是一个空列表。所以我希望各种mergeL和mergeR函数将该数组的元素移动到最终参数中 - 当它们全部被移动时,函数将基本终止(尽管最后调用反向函数)

这正是其余功能的作用:

mergeL([X|Xs],Ys,Zs) ->  mergeR(Xs,Ys,[X|Zs]);

获取X的第一个元素并将其放入Z数组中,并且

mergeR(Xs,[Y|Ys],Zs) ->  mergeL(Xs,Ys,[Y|Zs]);

获取Y的第一个元素并将其放入Z数组中。从mergeL调用mergeR,反之亦然,它正在进行交错部分。

有趣的是(并且易于修复)数组X和Y必须具有相同的长度,否则最终会调用mergeL或mergeR与X或Y中的空数组 - 这不会匹配[X | Xs]或[Y | YS]。

反向的原因仅仅是围绕[X |的相对效率Zs] vs [Zs | X]。前者效率更高。