【问题标题】:Emulate/use Continuations in JavaScript?在 JavaScript 中模拟/使用延续?
【发布时间】:2012-01-17 17:46:28
【问题描述】:

我有一个计算数组中数字乘积的函数。该功能应该像这样工作

function prod (array){
//compute and return product
}

var arr = [1,2,3,0,4,5,0,6,7,8,0,9];

the function call:
prod(arr); //should return 6
prod(arr); //should return 20
prod(arr); //should return 336 (6*7*8)
prod(arr); //should return 9
prod(arr); //should return 0
prod(arr); //should return 0
prod(arr); //should return 0

在方案中,这是通过延续来完成的,通过存储函数的先前状态(函数的状态在其退出点之前被捕获)see this

所以,简而言之,我希望 javascript 函数在不同时间返回不同的值,每次都传递相同的参数。

JavaScript 是一种设计良好的语言,所以我希望一定有一些东西可以模仿它。如果碰巧在 JS 中没有什么可以做的,我不介意以失败告终并继续前进。所以,随意说这是不可能的。

谢谢。

【问题讨论】:

  • 你不能有一个静态变量 keepmtrack 你的状态吗?
  • 这些数字的乘积永远为零,直到你把所有的零都去掉。
  • 不清楚 - 这些值是如何返回的?
  • 它返回所有数字的乘积直到下一个零,然后下一个调用从下一个零开始。

标签: javascript continuations


【解决方案1】:

JavaScript 无法支持延续:它缺少尾调用。

通常我会写这个来使用某种“队列”,尽管 CPS 也是可行的(只是有一个有限的堆栈:-) 请注意,其他状态也可以在闭包中捕获,使其成为“显式延续”之类的......在非常粗略的意义上。

使用闭包和队列的示例:

function prodFactory (array){
   // dupe array first if needed, is mutated below.
   // function parameters are always locally scoped.
   array.unshift(undefined)  // so array.shift can be at start
   // also, perhaps more closured state
   var otherState
   // just return the real function, yippee!
   return function prod () {
      array.shift()
      // do stuff ... e.g. loop array.shift() and multiply
      // set otherState ... eat an apple or a cookie
      return stuff
   }
}

var prod = prodFactory([1,2,3,0,4,5,0,6,7,8,0,9])

        // array at "do stuff", at least until "do stuff" does more stuff
prod()  // [1,2,3,0,4,5,0,6,7,8,0,9]
prod()  // [2,3,0,4,5,0,6,7,8,0,9]
prod()  // [3,0,4,5,0,6,7,8,0,9]

编码愉快。


“完成实施”。尽管这个特殊问题可以避免数组突变而只使用索引:同样的概念也适用。 (嗯,略有不同。只需一个索引,关闭的变量就会被改变,而使用这种方法,一个对象会发生变异。)

function prodFactory (array) {
   array = array.slice(0)
   return function prod () {
      var p = 1
      for (var n = array.shift(); n; n = array.shift()) {
        p *= n
      }
      return p
   }
}

var prod = prodFactory([1,2,3,0,4,5,0,6,7,8,0,9])

prod()  // 6
prod()  // 20
prod()  // 336

【讨论】:

  • 解决方案对我来说看起来不错。旁注:尾调用不是延续工作所必需的。 Rhino 支持延续——尽管在函数粒度上——我上次检查时。
  • @JohnClements 没错,我假设使用 [JavaScript] 语言级别而不是其他形式的堆栈操作 ;-) 这很有趣——我不知道任何 JS 引擎已经实现了它。
【解决方案2】:

你可以给函数一个在调用之间会被记住的属性:

function prod (array){
   if (typeof prod.index === "undefined" || prod.currentArray != array) {
      prod.currentArray = array;
      prod.index = 0;
   }

   if (prod.index >= array.length)
      return 0;

   //compute and return product
   var p = 1,
       c;
   while (prod.index < array.length) {
      c = array[prod.index++];
      if (c === 0)
         return p;
      p *= c;
   }
   return p;
}

我只是从您对应该返回什么的描述中猜测,在单独调用该函数时,它应该取所有数字的乘积,但不包括下一个零或数组的末尾。数组结束后的调用应该返回 0?我可能有错误的算法,但你明白我建议记住调用之间的函数状态的想法。

我添加了一个属性来记住当前正在处理的数组。只要您继续将相同的数组传递给函数,它将继续使用下一个元素,但如果您传递不同的数组,它将重置...

【讨论】:

    【解决方案3】:

    你可以试试

    var index = 0;
    function prod (array){
        if(index < array.length){
        var prod=1;
        for(int i=index;i<array.length;i++){
            if(array[i] != 0){
                prod = prod * array[i];
            }
            else{
                index = i+1;
                return prod;
            }
        }
    }
    return 0;   
    }
    

    这将在每次调用函数时更新全局变量索引。

    【讨论】:

      【解决方案4】:

      您在此处查找的是generators。从 1.7 开始,JavaScript supports them.

      【讨论】:

        猜你喜欢
        • 2017-07-15
        • 1970-01-01
        • 2020-05-01
        • 2011-10-22
        • 2021-06-06
        • 2012-03-29
        • 2010-09-18
        • 2018-08-29
        • 2016-06-30
        相关资源
        最近更新 更多