如何使用Javascript"延迟评估"什么时候想要避免急切的评价?

时间:2015-08-24 17:43:47

标签: javascript recursion functional-programming control-flow

这个例子取自本书 Javascript Allonge 。该主题涉及控制流操作员和功能参数评估。

const or = (a, b) => a || b

const and = (a, b) => a && b

const even = (n) =>
  or(n === 0, and(n !== 1, even(n - 2)))

even(42)
  //=> Maximum call stack size exceeded.

这本书指出这将导致无限递归。我相信我理解这一部分。由于将评估所有参数,即使或()中的 a 参数为true,仍会评估 b 参数(当在偶数()下调用或()函数,即。这同样适用于和()偶数(n - 2)参数最终将被反复评估, n 为2,0,-2,-4 ......

作为一种解决方案,它表示可以将匿名函数作为参数传递。

const or = (a, b) => a() || b()

const and = (a, b) => a() && b()

const even = (n) =>
  or(() => n === 0, () => and(() => n !== 1, () => even(n - 2)))

even(7)
  //=> false

现在,我理解代码是如何重写的,以便使用包含原始表达式的匿名函数,但我不知道如何延迟评估。"因为匿名函数仍然是或() even()函数的参数,所以要阻止它们评估并达到与之前相同的结果码?

2 个答案:

答案 0 :(得分:3)

  

因为匿名函数仍然是or()和even()函数的参数,是什么阻止它们评估并达到与前面代码相同的结果?

确实评估了or() / and()调用的参数。但这意味着函数表达式被计算(到函数),然后传递给or / and,而不是实际调用函数。它仅在函数内部从a() / b()调用,并且仅在实际需要时(由于操作符的短路) - 这就是实际进行递归调用的地方。

顺便说一句,这个概念也称为thunk

答案 1 :(得分:1)

even()的两个版本中,必须在调用or()之前评估外部调用or()的参数。因此,在第一个版本中:

const even = (n) =>
  or(n === 0, and(n !== 1, even(n - 2)))

必须计算参数表达式,第二个参数将触发无限递归。

然而,在第二个版本中,or()and()函数期望传递返回值的函数,而不是值本身。因此,在代码将其置于or()and()的实现中之前,函数不会被调用。因为编写or()and()函数是为了利用JavaScript短路逻辑运算符,所以没有无限递归。

因此:

const even = (n) =>
  or(() => n === 0, () => and(() => n !== 1, () => even(n - 2)))

仍然需要评估or()的实际参数,但参数只是函数 - 他们说如何获取值,但他们不知道实际做任何事情,直到他们被召唤。