来自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.
这是如何工作的?谢谢!
答案 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]。前者效率更高。