我是函数式编程的新手,我只是碰到了一些东西,并且想知道是否有办法解决这个问题。
假设我有
myArray = [
{ a : 1 }
{ a : 4 }
{ a : 5 }
{ a : 6 }
{ a : 7 }
{ a : 8 }
]
假设我需要对此数据集执行统计操作,例如
const median = myArray[ Math.ceil( myArray.length / 2 ) ]['a'] // Math.ceil .. Side Effect?
const fiveOrMore = myArray.filter( value => value.a >= 5 )
const lessThanFive = myArray.filter( value => value.a < 5 )
一些随意的例子。 现在的问题是,随着我需要做的统计操作量的增加,效率会降低。
凭借命令式的风格,我可以在 ONE 中完成所有操作。这是我正在采用的函数式编程的错误方法,还是函数式编程范式本身的权衡?
答案 0 :(得分:4)
凭借功能风格,您可以在一个减少中完成它。只需要一个检查元素在下面的函数,累加器就是一个带有两个列表的结构,你要添加元素。
如果您正在考虑通过一系列高阶函数传递列表,那么您可以使用Trancducers减少开销,这基本上类似于单个map
,filter
,但之间没有列表这些行动。
如果您不使用结果中的所有元素,则streams会使用延迟评估。
还有generators。基本上你可以制作几个for
循环并使用ỳield
来&#34;返回&#34;一个值,您可以链接这些,因为所有生成器都可以使用for of
进行迭代。此外,只要有足够的数据,您就可以暂停。
所有这些都有利有弊。如果你要使用生成器和流来计算所有元素将会有一点性能。传感器可能是一个更好的选择,它可以提供很少的列表制作的可组合性,但循环当然会更快。
通过功能实现,测试更容易,您可以测试隔离的各个阶段。规则整个应用程序的一个非常大的循环通常很难调试。当你有一个reduce
只重写一个功能样式的循环时,这也会发生。
答案 1 :(得分:3)
这当然不那么高效。 的性能是您选择的风格的折衷。
有人可能会说这不是什么大问题,因为时间复杂度是O(n)
,the only difference is the constant。我会说确保你对应用程序进行性能测试。如果它很慢 - 是时候优化某些代码块了。
过早优化是邪恶的。在许多情况下,命令式代码比功能性代码更快或更快地运行得快得多,并且根据情况你可能会或可能不会那样。
此外,还有各种技术可以改善性能。你不一定要改变风格。比方说,在某些情况下memoization可以大大提高函数的速度,同时保持代码的功能。只是在盒子外思考。
答案 2 :(得分:2)
你可以用功能或命令式的方式将事物塞进一个循环中。在两种样式中,它会损害可读性。在两种样式中,编译器可以执行loop fusion以消除额外的循环。此外,单个循环并不总是更快,并且编译器将能够辨别融合比人类更容易融合的情况。
正如Sylwester指出的那样,函数式编程有许多技术可以让你单独编写循环但是一起执行它们。功能样式也更容易显式并行化到多个线程上。
通常还有一些库函数已经在单个循环中完成了您想要的操作,并且更具描述性和简洁性。例如,您的最后两行可以使用partition:
完成_.partition(myArray, x => x['a'] < 5)
答案 3 :(得分:0)
我的座右铭之一是:
“我不在乎我能以多快的速度计算出错误的答案。”
我可能会尝试编写一些超快速代码,但是如果我的代码难以理解并因此容易引入错误/难以发现错误,那将是一个风险。除非代码对执行速度至关重要,否则我会选择最容易表达我想做的事情的方法:有时,功能性方法可以使您的意图非常明确,而在其他情况下,强制性方法则更合适。
一旦我的代码可以正常工作,并且通过了一组单元测试,然后可以根据需要重新编写,以提高速度。
顺便说一句,这就是为什么我现在喜欢Swift的原因-我同时提供功能和命令性选项。哦,我知道它不是纯粹的功能,但是在我的工程而非计算机科学的指导下,它已经足够接近了! ;-)