我正在阅读Concepts, Techniques, and Models of Computer Programming,并且在开头有一段代码,无论我怎么努力,我都无法理解。
declare Pascal AddList ShiftLeft ShiftRight
fun {Pascal N}
if N==1 then [1]
else
L in
L = {Pascal N-1} % Recursion
{AddList {ShiftLeft L}
{ShiftRight L}}
end
end
fun {ShiftLeft L}
case L of H|T then
H|{ShiftLeft T} % Recursion
else [0]
end
end
fun {ShiftRight L}
0 | L
end
fun {AddList L1 L2}
case L1 of H1|T1 then
case L2 of H2|T2
then
H1+H2|{AddList T1 T2} % Recursion
end
else nil
end
end
我得到了语言结构(这是对它的介绍),但真正阻碍我的是递归。
我正在尝试在每个递归调用上添加一个标签,它会抽象地说明这里发生了什么,但我无法理解它。
我要求的是对这些功能如何运作的清晰而简单的解释。
答案 0 :(得分:0)
从N == 1开始:这很简单。结果只是[1]
。
现在检查N == 2:
First we calculate L = {Pascal N-1} = {Pascal 2-1} = {Pascal 1} = [1]
Now shifted to the left: [1 0]
Shifted to the right: [0 1]
AddList just adds elementwise. So the result for {Pascal 2} is [1 1].
现在为N == 3:
{Pascal 2} = [1 1]
Shifted left: [1 1 0]
Shifted right: [0 1 1]
Added: [1 2 1]
当然,该程序的工作方式相反:它以较大的N
开头。但是在Pascal
函数的开头,程序会反复递归,直到参数N
变为1
。像这样:
{Pascal 3}
{Pascal 2}
{Pascal 1}
[1]
[1 1]
[1 2 1]
编辑:程序中实际上有各种递归。 Pascal
中的第一个以整数N
开头并递归到1
。
另一个(在辅助方法中)以包含头部和尾部的列表开始,一旦列表为空就停止,即不能再拆分。 (这是使用所谓的cons列表,一种本质上递归的数据类型。)
答案 1 :(得分:0)
wmeyer的解释非常好。我只想添加可能有用的“可视化”->
首先,我使用的是本书的原始版本(PDF),我相信,功能看起来像这样->
declare Pascal AddList ShiftLeft ShiftRight
fun {Pascal N}
if N==1 then [1]
else
{AddList {ShiftLeft {Pascal N-1}} {ShiftRight {Pascal N-1}}}
end
end
fun {ShiftLeft L}
case L of H|T then
H|{ShiftLeft T}
else [0] end
end
fun {ShiftRight L} 0|L end
fun {AddList L1 L2}
case L1 of H1|T1 then
case L2 of H2|T2 then
H1+H2|{AddList T1 T2}
end
else nil end
end
想象一下,您想看到Pascal三角形的第八行。您将输入:
{Browse {Pascal 8}}
即您想显示将book / here中定义的8馈送到Pascal函数的结果。
首先,该函数进行测试以查看刚传递的值是否为1(直到递归(或最终递归调用)的LAST迭代才为true,此时[1](从如果N == 1)将作为TALL CALL OF Pascal的输出返回,并且将执行的“链”向上(Pascal的)传递回下一个最近的调用(将结果[1]添加到)匹配的ShiftLeft或ShiftRight的结果,然后将那个结果发送回链,然后不断地发送,直到到达第一个(Pascal 8)为止。因此,通话深入8个级别,然后将答案传递回去这些水平,直到您得到最终答案为止。但是我跳了下去。
好吧,由于您输入了8,所以测试N == 1失败,因此,该函数无法移动'thelist'并将其立即添加到else子句中,因此该函数无法做到这一点在“等式”中用未定义的术语说:“我会尝试N-1!也许这将是最终答案!” (对于ShiftLeft和ShiftRight-因此,在每次recursino发生时都会发生此分支)
因此,该函数等待ShiftLeft和ShiftRight中Pascal N-1的答案...等待中,等待中...
好吧,对于N == 1,{Pascal 7}也将不成立,因此Pascal的较新电话(“电话”,第二和第三电话,左右!)都将问“什么是Pascal? N-1“(这次为7-1),他们都将等待答案...
这种情况不断发生……。哦,等等,直到N == 1!
然后返回[1](一个列表)BACK UP CHAIN ...因此,每个连续的等待函数调用(最近一次)(请记住,所有这些在下降的过程中都会越来越多地发生,直到到达此处为止)最终,随着分割数的增加,N == 1(通过每次调用ShiftLeft和ShiftRight一次)最终可以使其具有自己一直在等待的答案的AddList计算,包括自己对ShiftLeft和ShiftRight的私人调用
一切都从最底部开始,分解为越来越多的函数调用,然后我们回到顶部,最后可以得到返回的答案。最后的答案是第一次调用Pascal函数{Pascal 8}的else子句,该函数现在在内部(因为Pascal三角形的第八行是[1 7 21 35 35 21 7 1])看起来像:
{AddList [1 7 21 35 35 21 7 0] [0 7 21 35 35 21 7 1]} <-至少我认为这就是要添加的最终列表的样子
添加后的一个列表将作为最终答案返回并显示:[1 7 21 35 35 21 7 1]