我正在读一本书“Javascript中的函数式编程”。
在第二章中,在命令/函数代码之间进行了以下比较,用于查找字符串中只包含字母的前四个单词:
祈使
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的任何情况,命令式版本都会更快,因为它只运行到找到与标准匹配的前四个单词,而函数版本首先过滤整个数组,然后分割前四个元素。
我的问题是,我的假设是对的吗?
发布于 2016-02-13 22:59:37
将这两个代码片段进行比较非常有意义--作为教程的一部分。函数式编程要求很高,如果作者没有向他的读者展示最有效的函数实现,那么就让示例保持简单。
为什么函数式编程要求很高?因为它遵循数学原理(而这些原则并不总是人的逻辑),也因为新手习惯于有规律地使用祈使式。在FP中,数据流具有优先级,而实际的算法则停留在后台。习惯这种风格需要时间,但如果你做过一次,你可能永远也不会回头了!
如何以功能的方式更有效地实现此示例?有几种可能性,我举例说明其中两种。注意,这两种实现都避免了中间数组:
对Javascript进行严格的评估。然而,懒惰的评估可以模仿块(延髓功能)。此外,需要将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已经指出,执行速度不是函数式编程中最相关的问题。如果微观优化对您很重要,那么您应该继续依赖命令式风格。
发布于 2016-02-11 19:46:04
在某些情况下(像你的)是的,但并不总是这样。许多函数语言,如Haskell或Scala,都是在懒惰中构建的。这意味着函数不是立即评估的,而是只在需要时计算的。
如果您熟悉Java 8,那么它们的Streams也很懒,这意味着这样的东西不会遍历整个流3次。
stream.filter(n -> n < 200)
.filter(n -> n % 2 == 0)
.filter(n -> n > 15);这是一个非常有趣的概念,您可以查看Stream类的文档,这里是http://www.scala-lang.org/api/2.10.0/index.html#scala.collection.immutable.Stream
https://stackoverflow.com/questions/35348410
复制相似问题