在javascript函数中使用函数式Haskell-like Accumulator
Use of functional Haskell-like Accumulator in javascript functions
我最近正在研究Haskell,我对它的一些特性很着迷,例如使用累加器的末端递归函数。
问题:
- 在javascript中是否有类似的结构?或者它甚至在效率方面是有意义的,因为javascript不是as功能像Haskell? 有没有像ramda, lodash,…这样的图书馆?支持这种方式编程的
如果是,你会如何写,例如在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)
在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
循环。
这是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。你可以用你的就像我上面展示的那样。还要注意lodash是提供用于操作的一致API的实用程序库以功能的方式收集。
除了Sibi的回答,我想指出javascript(至少nodejs)实际上分配堆栈空间。它工作得很好,很快,直到大约13000的指数,然后你会得到RangeError: Maximum call stack size exceeded
。要进行这个实验,你需要将基数设置为接近1的数字(例如1.0001),否则你会得到无穷大。
Haskell没有这个问题。1000倍大的指数(即13,000,000)仍然不会导致任何空间问题,尽管它确实需要几秒钟才能运行。这是因为递归是尾部调用,而这些调用在haskell中是在常量空间中运行的。
所以在某种程度上,Sibi的回答模仿了haskells的表达能力,但它仍然表现出不同的运行时行为。我认为你对此无能为力。我同意所有的答案,即库既不是必需的,也不是特别有用。(顺便说一句,我是Ramda的作者之一。)
Bergi对JS的翻译很好,尽管我认为至少在浏览器端JS中更习惯,将helper函数嵌入到局部闭包中,这更接近于Sibi的答案。
Martin Drautzburg指出的性能问题的原因是,尽管指定了尾部调用优化,但它几乎没有在任何地方实现。一个例外是Babel对直接递归的支持,因此Babel编译的版本应该获得预期的性能优势。
所以,如果你想这样做是因为它的优雅,因为你相信TCO很快就会到来,如果你不担心当前可能出现的性能问题,那么这些回答是有用的,我甚至会再加一个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
默认函数参数可以帮助替换这种语言中一些简单形式的模式匹配,因为它没有真正的模式匹配。这仍然依赖于TCO,但使代码更简洁。
- 创建一个类似链接的按钮,并通过Javascript函数打开一个新的弹出窗口
- 将函数的上下文应用于javascript变量
- 如何在JavaScript中将字符串转换为函数引用
- 用嵌套函数和默认函数定义函数
- 使用 jQuery 的 .on 函数如何获取事件的原始元素
- 无法导出函数expressjs/requestjs中的变量
- 函数参数中的数据与指定变量之间的任何性能差异
- JQuery合并了keyup和focusout两个函数
- ES6构造函数返回基类的实例
- 监视函数从服务返回不起作用,但作用域函数起作用
- 我可以在json对象中添加一个函数吗
- AngularJS:我可以跳过函数参数回调吗
- 如何使jQuery插件函数可调用以供独立使用,而不在集合上操作
- JavaScript数组排序(函数)用于对表行进行排序,而不是排序
- jquery点击函数select&取消选择
- 使用 PHP 执行“javascript/jQuery-like”函数
- Haskell中的可变/可选空气度函数;自变量对象“;反省
- JavaScript和Haskell的函数组合
- 只运行函数一次(显示toast like message) javascript/jquery
- 在javascript函数中使用函数式Haskell-like Accumulator