首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在这种情况下,函数式编程是否效率较低?

在这种情况下,函数式编程是否效率较低?
EN

Stack Overflow用户
提问于 2016-02-11 19:31:25
回答 2查看 618关注 0票数 15

我正在读一本书“Javascript中的函数式编程”。

在第二章中,在命令/函数代码之间进行了以下比较,用于查找字符串中只包含字母的前四个单词:

祈使

代码语言: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++;
  }
}

功能

代码语言:javascript
复制
var words = [];
var words = myString.split(' ').filter(function(x){
    return (! x.match(/[1-9]+/));
}).slice(0,4);

我的推理是,对于text长度大于4的任何情况,命令式版本都会更快,因为它只运行到找到与标准匹配的前四个单词,而函数版本首先过滤整个数组,然后分割前四个元素。

我的问题是,我的假设是对的吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-02-13 22:59:37

将这两个代码片段进行比较非常有意义--作为教程的一部分。函数式编程要求很高,如果作者没有向他的读者展示最有效的函数实现,那么就让示例保持简单。

为什么函数式编程要求很高?因为它遵循数学原理(而这些原则并不总是人的逻辑),也因为新手习惯于有规律地使用祈使式。在FP中,数据流具有优先级,而实际的算法则停留在后台。习惯这种风格需要时间,但如果你做过一次,你可能永远也不会回头了!

如何以功能的方式更有效地实现此示例?有几种可能性,我举例说明其中两种。注意,这两种实现都避免了中间数组:

  1. 懒惰评价

对Javascript进行严格的评估。然而,懒惰的评估可以模仿块(延髓功能)。此外,需要将foldR (折叠右)作为迭代函数,由此导出filterN

代码语言:javascript
复制
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)。

  1. 延拓传递方式

CPS启用了filterN的纯变体。

代码语言:javascript
复制
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"]

foldRfoldL之间的区别有点让人困惑。区别不在于交换性,而在于结合性。CPS的实现仍然有一个缺点。filterN应该分为filtertakeN,以提高代码的可重用性。

  1. 换能器

传感器允许组合(减少/转换)功能,而不必依赖中间阵列。因此,我们可以将filterN分为两个不同的函数filtertakeN,从而提高了它们的可重用性。不幸的是,我还没有找到一个适用于可理解和可执行的示例的传感器的简明实现。我将尝试开发我自己的,简化的传感器解决方案,然后在这里给出一个适当的例子。

结论

如您所见,这些实现可能不如命令式解决方案有效。Bergi已经指出,执行速度不是函数式编程中最相关的问题。如果微观优化对您很重要,那么您应该继续依赖命令式风格。

票数 4
EN

Stack Overflow用户

发布于 2016-02-11 19:46:04

在某些情况下(像你的)是的,但并不总是这样。许多函数语言,如Haskell或Scala,都是在懒惰中构建的。这意味着函数不是立即评估的,而是只在需要时计算的。

如果您熟悉Java 8,那么它们的Streams也很懒,这意味着这样的东西不会遍历整个流3次。

代码语言:javascript
复制
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

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35348410

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档