【问题标题】:Javascript lazy evaluation fibonacci functionJavascript惰性求值斐波那契函数
【发布时间】:2013-06-26 19:44:56
【问题描述】:

我之前问过a question,关于在 Scala 中使用惰性求值。我试图在 Scala 中编写以下 Haskell 函数:

fib a b = c : (fib b c)
   where c = a+b

这个问题的答案是我不能使用 Lists,而应该使用 Streams。现在我正在尝试在 Javascript 中做同样的事情。我翻译了函数,在this site上试了一下:

function fib(a,b) {
    c = a+b;
    return [c] + fib(b,c);
}

var res = fib(0,1).slice(0,10);

console.log(res);

但我收到以下错误:

RangeError: Maximum call stack size exceeded

Javascript 有办法做到这一点吗?

【问题讨论】:

  • 结束条件在哪里?
  • 您的递归没有退出条件。
  • 想法是这个例子中的10个数字之后,列表将不再被评估。
  • 我现在开始明白slice() 不会那样做。
  • @RobertHarvey 确实如此。您的链接对 OP 非常有帮助。我只是在解释为什么在题为“惰性评估”的问题中,OP 故意不包含递归的退出条件。在发表评论时,您似乎忽略了惰性评估方面。也许在说明退出条件的需要之前明确的“javascript没有延迟评估”会更清楚。

标签: javascript haskell lazy-evaluation


【解决方案1】:

您可以具体化惰性计算正在使用的 thunk(阅读:“尚未评估的继续计算的函数”)。

var fib = function (a, b) {
  var c = a + b
  return { "this": c, "next": function () { return fib(b, c) } }
}

这样

> var x = fib(1,1)
> x.this
2
> x = x.next()
> x.this
3

从某种意义上说,这是一个精确的翻译*,其中我的返回对象代表一个 Haskell (:) "cons" 单元格。从这里开始编写一个“take”函数来将这个javascript“惰性列表”转换为一个javascript严格数组会相对容易。

这是一个版本。

var take = function(n, cons) {

    var res = []
    var mem = cons

    for (var i = 0; i < n; i++) {
      res.push(mem.this)
      mem = mem.next()
    }

    return res
}

这样

> take(10, fib(1,1))
[2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

(*) 从技术上讲,即使是 "this" 值也应该包含在一个 thunk 中,但我采用了列表的严格概念,这通常非常接近每个人的直觉。

【讨论】:

  • +1,thunk 是这样做的正确方法。太糟糕了 JS 没有宏或者你可以写 stream-cons 和 stream-cdr
  • 我也说javascript没有类型太糟糕了,否则我们可以定义LazyList a = {"this": a, "next": function () (returns: LazyList a)}:)
【解决方案2】:

不完全是 Haskell 惰性求值,但你可以用柯里化做类似的事情。

function fib(a,b) {
    var first = true;

    return function(n) {
        if (!isFinite(n) || n < 0)
            throw "Invalid argument";

        var result = first ? [a,b] : [];
        first = false;

        for (var i = result.length; i < n; i++)
            result.push(b = a+(a=b));

        return result;
    };
}

返回的函数可以被多次调用以获得连续的结果:

var f = fib(0,1); // Initialize the sequence with starting values

// Invoke the resulting function with the number of results you want
console.log(f(10)); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

console.log(f(4));  // [55, 89, 144, 233]

console.log(f(4));  // [377, 610, 987, 1597]

【讨论】:

    【解决方案3】:

    ES6 有generator functions,你可以用它来进行惰性求值(结合destructuring assignment operator) 使用这种技术,我们可以编写如下所示的斐波那契函数(只是另一种方法)。

    var fib_generator = function *(){
      var current = 0, next = 1;
      while(true)
      {
         [next, current] = [next+current, next];
         yield current;
      }
    }
    
    var fib = fib_generator();
    
    
    // below is the sample code prints values uptill 55
    for(var i=0; i<10; i++)
    {
       console.log(fib.next().value);
    }

    【讨论】:

    • @melpomene 谢谢。抱歉,我假设对变量而不是函数本身的惰性求值,没有正确阅读问题。
    猜你喜欢
    • 2018-10-08
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 2014-05-03
    • 2014-05-19
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    相关资源
    最近更新 更多