我之前在Scala中使用延迟评估之前询问过a question。我试图在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有办法做到这一点吗?
答案 0 :(得分:10)
你可以重新启动延迟计算正在使用的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“lazy list”转换为javascript strict数组是相对容易的。
这是一个版本。
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 :(得分:2)
不完全是Haskell懒惰评估,但你可以做一些与currying相似的事情。
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]
答案 2 :(得分:0)
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);
}