Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用 JavaScript 中的蹦床函数实现安全递归 #7063

Merged
merged 3 commits into from
May 19, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions article/2020/safe-recursion-with-trampoline-in-javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
> * 原文作者:[Valeriy Baranov](https://medium.com/@baranovxyz)
> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/article/2020/safe-recursion-with-trampoline-in-javascript.md](https://github.com/xitu/gold-miner/blob/master/article/2020/safe-recursion-with-trampoline-in-javascript.md)
> * 译者:
> * 校对者:
> * 译者:[Gesj-yean](https://github.com/Gesj-yean)
> * 校对者:[cyz980908](https://github.com/cyz980908),[z0gSh1u](https://github.com/z0gSh1u)

# Safe Recursion with Trampoline in JavaScript
# 使用JavaScript中的蹦床函数实现安全递归

![Photo by [Charles Cheng](https://unsplash.com/@charlesc7?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/7274/0*9Sxt2ppwVpNELxC0)
![来自[Charles Cheng](https://unsplash.com/@charlesc7?utm_source=medium&utm_medium=referral) [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/7274/0*9Sxt2ppwVpNELxC0)

Using recursion in JavaScript is not safe — consider trampoline. This converts recursion to a `while` loop to get around JavaScript’s limitations and prevent an overflow.
JavaScript 中使用递归是不安全的,请考虑使用蹦床函数。它将递归转化为 `while` 循环去绕过 JavaScript 的限制,目的是为了防止溢出。

An idiomatic example of a recursive function is a factorial calculation. We call `factorial` function `n` times to get a result. Every call will add one more `factorial` function to the call stack.
一个典型的递归函数就是阶乘计算。我们调用 `factorial` 函数 `n` 次来得到一个结果。每次调用都会向调用的堆栈添加一个 `factorial` 函数。

```JavaScript
function factorial(n) {
Expand All @@ -22,7 +22,7 @@ function factorial(n) {
console.log(factorial(5)); // 120
```

Recursion in JavaScript is mostly a sign of code smell and generally should be avoided. The reason is that JavaScript is a stack-based language, there is no tail call optimization yet. If you execute the following snippet, you will get a RangeError: Maximum call stack size exceeded.
JavaScript 中的递归通常是代码出现问题的象征,通常应该避免使用。因为 JavaScript 是一种基于堆栈的语言,但目前没有尾调用优化。如果你运行了以下代码,将会报错 RangeError: Maximum call stack size exceeded,意思是超过最大调用堆栈大小。

```JavaScript
function recursiveFn(n) {
Expand All @@ -33,7 +33,7 @@ function recursiveFn(n) {
recursiveFn(100000); // RangeError: Maximum call stack size exceeded
```

Still, there are situations when you want to use a recursive function. A realistic one would be to traverse a `DOM` tree or any other tree-like structure.
不过,也有需要使用递归函数的情况。例如,遍历 `DOM` 树或其他的树形结构。

```JavaScript
function traverseDOM(tree) {
Expand All @@ -42,9 +42,9 @@ function traverseDOM(tree) {
}
```

#### Trampoline — a safe way to use recursion in JavaScript.
#### 蹦床函数 —— JavaScript中的安全递归方式
Gesj-yean marked this conversation as resolved.
Show resolved Hide resolved

With `trampoline`, your transform recursive function into a `while` loop:
使用 `蹦床函数`,你可以将递归函数转化为 `while` 循环:

```JavaScript
function trampoline(f) {
Expand All @@ -58,12 +58,12 @@ function trampoline(f) {
}
```

Let's rewrite the `factorial` function to use `trampoline`. We need to:
让我们用 `蹦床函数` 去重写一个 `阶乘` 函数。我们需要去:

1. return a value in a base case.
2. return a function to be called in other cases.
1. 在基本情况下返回一个值。
2. 在其他情况下返回要调用的函数。

We also add accumulator argument for our internal implementation of `_factorial`.
我们还为 `_factorial` 的内部实现添加了累加器参数。

```JavaScript
const factorial = trampoline(function _factorial(n, acc = 1) {
Expand All @@ -74,7 +74,7 @@ const factorial = trampoline(function _factorial(n, acc = 1) {
console.log(factorial(5));
```

Our `recursiveFn` can be rewritten similar to `factorial`, but we do not need an accumulator to track state. This implementation can be safely run even for millions of iterations.
`recursiveFn` 可以像 `factorial`一样被重写,但我们不需要累加器去跟踪状态。这个实现方式甚至可以安全的运行数百万次的迭代。

```JavaScript
const recursiveFn = trampoline(function _recursiveFn(n) {
Expand All @@ -86,15 +86,15 @@ recursiveFn(100000);
console.log('No range error!');
```

Next time you want to use recursion, try `trampoline`!
下一次你想使用递归的时候,试试 `蹦床函数` 吧!

You can try `trampoline` in this [CodePen](https://codepen.io/baranovxyz/pen/zYvjKGN).
你可以尝试 `蹦床函数` 在这里 [CodePen](https://codepen.io/baranovxyz/pen/zYvjKGN)

---

Trampoline is not a popular technique. Even in ramda and lodash, there are no trampoline helpers.
蹦床函数并不是一种流行的技术。即使在 ramda lodash 中,也没有提供蹦床函数。

Thanks to Kyle Simpson for the inspiration about trampolines.
感谢 Kyle Simpson 为我提供了蹦床函数的灵感。

> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。

Expand Down