在这种情况下,函数式编程的效率是否较低
Is functional programming less efficient for this case?
我正在阅读"Javascript中的函数式编程"一书。
在第 2 章中,对命令性/功能代码进行了以下比较,用于查找字符串中仅包含字母的前四个单词:
祈使的
var words = [], count = 0;
text = myString.split(' ');
for (i=0; count<4, i<text.length; i++) {
if (!text[i].match(/[0-9]/)) {
words = words.concat(text[i]);
count++;
}
}
功能的
var words = [];
var words = myString.split(' ').filter(function(x){
return (! x.match(/[1-9]+/));
}).slice(0,4);
我推断,对于任何text
长度大于 4 的情况,命令式版本都会更快,因为它只运行到找到符合条件的前四个单词,而函数版本首先过滤整个数组,然后才切开前四个元素。
我的问题是,我假设这个对吗?
在某些情况下(如您的(是的,但并非总是如此。许多函数式语言,如Haskell或Scala,都内置了懒惰。这意味着函数不会立即评估,而只会在需要时进行评估。
如果你熟悉Java 8,他们的Streams API也是懒惰的,这意味着这样的东西,不会遍历整个流3次。
stream.filter(n -> n < 200)
.filter(n -> n % 2 == 0)
.filter(n -> n > 15);
这是一个非常有趣的概念,你可以在这里查看Scala Stream类的文档 http://www.scala-lang.org/api/2.10.0/index.html#scala.collection.immutable.Stream
作为教程的一部分,这两个代码片段的比较非常有意义。函数式编程要求很高,如果作者没有用最有效的函数式实现来面对他的读者,那么保持示例简单。
为什么函数式编程要求很高?因为它遵循数学原理(这些并不总是人类逻辑(,并且因为新手习惯于定期使用命令式风格。在FP中,数据流具有优先级,而实际算法保留在后台。习惯这种风格需要时间,但如果你做过一次,你可能永远不会回头!
如何以功能方式更有效地实现此示例?有几种可能性,其中我说明两种。请注意,这两种实现都避免了中间数组:
- 惰性求值
Javascript是严格评估的。但是,惰性求值可以使用 thunks(空函数(来模拟。此外,需要foldR
(右折叠(作为推导filterN
的迭代函数:
const foldR = rf => acc => xs => xs.length
? rf(xs[0])(() => foldR(rf)(acc)(xs.slice(1)))
: acc;
const filterN = pred => n => foldR(
x => acc => pred(x) && --n ? [x].concat(acc()) : n ? acc() : [x]
)([]);
const alpha = x => !x.match(/[0-9]/);
let xs = ["1", "a", "b", "2", "c", "d", "3", "e"];
filterN(alpha)(4)(xs); // ["a", "b", "c", "d"]
此实现的缺点是filterN
不是纯的,因为它是有状态的(n
(。
- 延续传递样式
CPS支持filterN
的纯变体:
const foldL = rf => acc => xs => xs.length
? rf(acc)(xs[0])(acc_ => foldL(rf)(acc_)(xs.slice(1)))
: acc;
const filterN = pred => n => foldL(
acc => x => cont => pred(x)
? acc.length + 1 < n ? cont(acc.concat(x)) : acc.concat(x)
: cont(acc)
)([]);
const alpha = x => !x.match(/[0-9]/);
let xs = ["1", "a", "b", "2", "c", "d", "3", "e"];
filterN(alpha)(4)(xs); // ["a", "b", "c", "d"]
foldR
和foldL
有何不同有点令人困惑。区别不在于交换性,而在于结合性。CPS实现仍然有一个缺点。 filterN
应分为filter
和takeN
,以增加代码的可重用性。
- 传感器
换能器允许组成(减少/变换(功能,而不必依赖中间阵列。因此,我们可以将filterN
分为两个不同的函数filter
和takeN
,从而提高它们的可重用性。不幸的是,我还没有找到适合可理解和可执行示例的换能器的简洁实现。我将尝试开发我自己的简化传感器解决方案,然后在这里给出一个适当的示例。
结论
如您所见,这些实现可能不如命令式解决方案高效。Bergi已经指出,执行速度并不是函数式编程最相关的问题。如果微优化对您很重要,您应该继续依赖命令式样式。
- 访问布局信息是否也会导致浏览器重排
- 当包含另一个asp文件时,是否也包含所有引用的样式和脚本页面
- 如何检测是否有溢出
- jQuery中是否内置了任何字符串格式化函数
- 是否有任何snippet或jQuery插件可以列出easylist.txt模式匹配的DOM中的所有元素
- 测试索引值是否等于某个数字的倍数
- Fancybox是否将Click事件静音
- 主干-不管怎样,检查事件以前是否绑定过
- YUI3 IO实用程序是否可以根据给定的内容类型标头值自动序列化数据
- 是否有一个JS/jQuery函数可以获取某个类的每个元素的ID
- 节点是否需要模块传递带有方括号的arg?这是个错误吗
- 是否有任何方法可以使用jQuery替换在数组中定义值的文本
- 是否可以将一个函数输入连接到另一个函数调用的文本
- 是否可以禁用jquery中的单个单选按钮
- 是否可以从父类访问子类的属性
- 是否可以控制获取哪些Google地图脚本(JavaScript API)
- 如何让程序检查所选单词中是否有按键
- 用于检查数组中是否存在元素的javascript自定义方法
- 在这种情况下,函数式编程的效率是否较低
- 用JavaScript/jquery按id查找和按类查找之间是否存在效率差异?