我目前正在研究Haskell,我对它的一些功能非常着迷,例如使用累加器的结束递归函数。
问题:
如果是这样,您将如何在javascript中编写此代码:
power_acc :: Double -> Int -> Double
power_acc x y = power_acc_h x y 1
power_acc_h :: Double -> Int -> Double -> Double
power_acc_h x 0 acc = acc
power_acc_h x y acc = power_acc_h x (y-1) (acc*x)
答案 0 :(得分:4)
这是javascript中Haskell代码的直接翻译:
function power_acc(x, y) {
return aux(x,y,1);
function aux(x, y, acc) {
if (y == 0)
return acc;
else
return aux(x, y-1, acc*x);
}
}
是否有像ramda,lodash ......这样的库支持这个 编程方式?
你不需要lodash或ramda。你可以用你的 正如我上面所示,简单的javascript。还要注意lodash是 一个实用程序库,提供一致的API来操作 集合以功能的方式。在这些情况下,它不会帮助你。
答案 1 :(得分:4)
javascript中是否有类似的构造?
是的,您可以直接将其翻译为JS:
function power_acc(x, y) { // Double -> Int -> Double
y = y>>>0; // cast to positive int (avoiding nontermination)
return power_acc_h(x, y, 1);
}
function power_acc_h(x, y, acc) { // Double -> Int -> Double -> Double
return y == 0
? acc
: power_acc_h(x, y-1, acc*x);
}
或者它甚至对效率有意义,因为javascript不如Haskell那么实用?
使用ES6,JS中完全支持尾递归,你将获得与循环相同的效率(可能甚至比haskell更好,因为你不创建延迟乘法)。
是否有像ramda,lodash ......这样的库支持这种编程方式
无需图书馆。虽然我确信有些库可以简化类型检查或为模式匹配提供更好的符号。
你会如何在javascript中写这个?
您使用while
循环。 haskell中的所有累加函数都是以这种方式编写的,因为它们可以直接优化为循环,这就是JS中这个构造应该使用的符号(大多数程序员都熟悉它):
function power_acc(x, y) { // Double -> Int -> Double
y = y>>>0; // cast to positive int (avoiding nontermination)
var acc = 1;
while (y != 0) {
acc *= x;
y -= 1;
}
return acc;
}
变异局部变量没有害处,你的功能仍然是纯粹的。如果您正在寻找更短的符号,请使用for
循环。
答案 2 :(得分:3)
除了Sibi的回答,我还想指出javascript(至少是nodejs)实际上分配了堆栈空间。它工作正常,快到大约13,000的指数,然后你得到RangeError: Maximum call stack size exceeded
。要执行此实验,您需要将基数设置为接近1的数字(例如1.0001),否则您将获得无穷大。
Haskell没有遇到这个问题。指数1000倍(即13,000,000)仍然不会导致任何空间问题,尽管它需要几秒钟才能运行。这是因为递归是一个尾调用,它们在haskell中以恒定的空间运行。
所以在某种程度上,Sibi的回答是mimicks具有表现力,但它仍然表现出不同的运行时行为。我不认为你可以做任何事情。
答案 3 :(得分:3)
我同意所有答案,因为图书馆既不是必需的,也不是特别有帮助的。 (我是Ramda的作者之一,BTW。)
Bergi对JS的翻译很好,虽然我认为它至少在浏览器端JS中更为惯用,将辅助函数嵌入到本地闭包中,更接近Sibi&#39的答案。
Martin Drautzburg指出的性能问题的原因是,虽然尾部调用优化is specified,但它在任何地方都是barely implemented。 Babel对直接递归的支持是一个例外,因此Babel转换后的版本应该会获得预期的性能优势。
因此,如果您想要这样做是因为优雅,并且因为您认为TCO会很快出现,并且如果您不担心当前可能出现的性能问题,那么这些响应很有用,我会&#39 ;甚至会再添加一个ES6技术:
// Double -> Int -> Double -> Double
function powerAcc(x, y, acc = 1) {
return y == 0 ? acc : powerAcc(x, y - 1, acc * x);
}
powerAcc(2, 5); //=> 32
Default function parameters帮助替换这种语言中的一些简单形式的模式匹配,它们没有真正的模式匹配。这仍然依赖于TCO,但使代码更清晰。它也应该在Babel中运行。