@@ -1126,19 +1126,16 @@ \subsubsection{尾递归}
1126
1126
\index {Tail call}
1127
1127
\index {Tail recursion}
1128
1128
\index {Tail recursive call}
1129
- Note that both sum and product algorithms actually compute the result from right to left. We can change them
1130
- to the normal way, that calculate the {\em accumulated} result from left to right. For example with sum,
1131
- the result is actually accumulated from 0, and adds element one by one to this accumulated result till
1132
- all the list is consumed. Such approach can be described as the following.
1133
1129
1134
- When accumulate result of a list by summing:
1130
+ 注意到无论是求和还是求积的算法都从右向左计算。我们可以修改它们的实现,从左向右\underline {累积计算}结果。求和时,结果从0开始累积,逐一将每个元素加到结果上,直到处理完全部列表。具体描述如下:
1131
+
1132
+ 当通过求和累积结果时:
1135
1133
\begin {itemize }
1136
- \item If the list is empty, we are done and return the accumulated result;
1137
- \item Otherwise, we take the first element from the list, accumulate it to the result by summing, and go on
1138
- processing the rest of the list.
1134
+ \item 若列表为空,则累积结束,返回累积结果;
1135
+ \item 否则,取出列表中的第一个元素,将其加到累积结果上,然后继续处理剩余的列表。
1139
1136
\end {itemize }
1140
1137
1141
- Formalize this idea to equation yields another version of sum algorithm.
1138
+ 将这一描述形式化为定义,就可以得到另一种累加的算法。
1142
1139
1143
1140
\be
1144
1141
sum'(A, L) = \left \{
@@ -1150,25 +1147,16 @@ \subsubsection{尾递归}
1150
1147
\right .
1151
1148
\ee
1152
1149
1153
- And sum can be implemented by calling this function by passing start value 0 and the list as arguments.
1150
+ 最终求和可以通过调用这一函数实现。我们传入0作为累加的起始值,同时传入待累加的列表。
1154
1151
\be
1155
1152
sum(L) = sum'(0, L)
1156
1153
\ee
1157
1154
1158
- The interesting point of this approach is that, besides it calculates the result in a normal order from
1159
- left to right; by observing the equation of $ sum'(A, L)$ , we found it needn't remember any intermediate
1160
- results or states when perform recursion. All such states are either passed as arguments ($ A$ for example)
1161
- or can be dropped (previous elements of the list for example). So in a practical implementation,
1162
- such kind of recursive function can be optimized by eliminating the recursion at all.
1155
+ 这一改进除了将计算的顺序恢复为从左向右之外,还有一个重要的特点。观察函数$ sum'(A, L)$ 的定义,我们发现它无需记录任何中间结果或者状态用于递归。所有的状态或者作为参数(例如$ A$ )传入接下来的递归调用,或者可以丢弃(例如列表中前面处理过的元素)。因此在实际的实现中,这样的递归函数可以进一步优化为循环,从而完全消除递归。
1163
1156
1164
- We call such kind of function as `tail recursion' (or `tail call'), and the optimization of removing recursion in this case as
1165
- 'tail recursion optimization' \cite {wiki -tail -call } because the recursion happens as the final action
1166
- in such function. The advantage of tail recursion optimization is that the performance can be greatly
1167
- improved, so that we can avoid the issue of stack overflow in deep recursion algorithms such as sum and
1168
- product.
1157
+ 我们称这样的函数为“尾递归”(或“尾调用”),对其消除递归的优化称为“尾递归优化”\cite {wiki -tail -call }。顾名思义,这类函数中,递归发生在最后一步。尾递归优化可以极大地提高性能,并避免由于过深递归造成的调用栈溢出。
1169
1158
1170
- Changing the sum and product Haskell programs to tail-recursion manner gives the following modified
1171
- programs.
1159
+ 下面的Haskell例子程序给出了尾递归形式的求和与求积实现。
1172
1160
1173
1161
\lstset {language=Haskell}
1174
1162
\ begin{lstlisting}
@@ -1181,8 +1169,7 @@ \subsubsection{尾递归}
1181
1169
product' acc (x:xs) = product' (acc * x) xs
1182
1170
\end {lstlisting }
1183
1171
1184
- In previous section about insertion sort, we mentioned that the functional version sorts the elements
1185
- form right, this can also be modified to tail recursive realization.
1172
+ 在前面关于插入排序的部分,我们提到了函数式的实现从右向左对元素排序,我们也可以将其改为尾递归的形式。
1186
1173
1187
1174
\be
1188
1175
sort'(A, L) = \left \{
@@ -1194,15 +1181,14 @@ \subsubsection{尾递归}
1194
1181
\right .
1195
1182
\ee
1196
1183
1197
- The the sorting algorithm is just calling this function by passing empty list as the accumulator argument.
1184
+ 排序时,我们可以调用这一函数,传入一个空列表作为累积结果的起始值。
1198
1185
\be
1199
1186
sort(L) = sort'(\phi , L)
1200
1187
\ee
1201
1188
1202
- Implementing this tail recursive algorithm to real program is left as exercise to the reader.
1189
+ 我们将它的具体实现作为练习留给读者。
1203
1190
1204
- As the end of this sub-section, let's consider an interesting problem, that how to design an algorithm
1205
- to compute $ b^n$ effectively? (refer to problem 1.16 in \cite {SICP }.)
1191
+ 作为本节的结尾,我们考虑一个有趣的题目,如何设计一个算法来高效地计算$ b^n$ ?(参考\cite {SICP }中的1.16节。)
1206
1192
1207
1193
A naive brute-force solution is to repeatedly multiply $ b$ for $ n$ times from 1, which leads to a
1208
1194
linear $ O(n)$ algorithm.
0 commit comments