我正在观看Misko Hevery关于干净代码的讨论,他提到尝试编写一个程序,其中没有if语句(好,尽可能少)以模拟工作... Smalltalk或者一些这种语言,其中多态性优于内联条件行为。
由于我的理解有限,函数式编程对于像我这样只是命令式的程序员来说很难 - 因为我们的状态变化方法没有办法在功能程序中表达。函数只接受一个值并返回一个值,并且对状态一无所知。
我也看到JS被誉为能够支持功能模型。
那么是否有一套简单的限制,类似于我的第一段,这将使我能够用我熟悉和喜爱的语言尝试功能范例 - 而不是学习一种全新的语言(我最终会这样做)但我现在想尝试一下这种风格)?
答案 0 :(得分:5)
“函数式编程”一词有两个含义。
第一个含义是程序操作函数的能力。并非所有语言都可以这样做,但javascript是可以使用的语言之一。可以将函数赋值给变量,将函数传递给参数和返回函数的语言称为函数式编程语言,因此javascript按原样运行。
从这个意义上说,如果你看一下流行使用回调的任何现代javascript代码,那么你已经在进行函数式编程了。
函数式编程的第二个含义是编程风格,其中程序组合的主要方法是函数而不是变量。从这个意义上讲,几乎所有语言都可以通过避免变量赋值和循环结构而在函数式中使用(改为使用递归)。
当你看到功能社区时,他们所说的功能是第一个含义加上第二个意义的强大版本 - 也就是说,变量不仅被避免而且被禁止。像Haskell这样的语言没有变量的概念。为了处理像I / O这样的副作用和可变状态,他们使用了一个名为monads的概念。
你不必那么远。像Lisp和Forth这样的经典函数语言允许变量。你必须尽可能避免使用它们。
Lisp和Forth风格的函数式编程很大程度上是由处理列表/数组驱动的,没有为临时变量赋值。对某些人来说,这种风格更容易阅读。在命令式的情况下你会这样做:
var a = [1,2,3];
var b = 0;
for (var i=0;i=a.length;i++) {
b += a[i] * 2;
}
// result is in b
以功能方式执行此操作:
[1,2,3].
map(function(x){return x*2}).
reduce(function(x,y){return x+y},0);
从概念上讲,功能样式使得它看起来像是在对数组应用过滤器而不是遍历数组。如果您曾经使用过grep
之类的命令行工具,那么您会发现这个概念非常熟悉。
请注意,我们在功能样式中根本没有引入任何变量赋值。
功能样式中的三个核心数组方法/函数是:map
,reduce
和filter
。有了它们,你可以避免90%的for循环。
答案 1 :(得分:2)
好问题。
要获得深入的答案,请查看John Hughes撰写的这篇经典(和令人敬畏的)论文 - Why Functional Programming Matters。
首先,Hughes解释了不的函数式编程,并引用了FP使用支持者的一些弱论据。好东西(但这里引用的时间太长了。)
其次,Hughes解释了FP的含义:
现在普遍认为,模块化设计是成功编程的关键,最近的语言如Modula-II [6]和Ada [5]包含专门用于帮助改进模块化的功能。但是,有一个非常重要的一点经常被遗漏。在编写模块化程序来解决问题时,首先将问题划分为子问题,然后解决子问题,最后组合解决方案。 人们可以分解原始问题的方式直接取决于人们将解决方案粘合在一起的方式。因此,为了提高一个人在概念上模块化问题的能力,必须在编程语言中提供新的粘合剂。复杂的范围规则和单独编译的规定只对文书细节有帮助 - 他们永远无法做出很大的贡献。模块化。
我们将在本文的其余部分争论说,函数式语言提供了两种新的非常重要的粘合剂。我们将给出一些程序的例子,这些程序可以以新的方式进行模块化,从而可以简化。这是功能编程功能的关键 - 它可以改善模块化。这也是功能程序员必须努力的目标 - 更小,更简单,更通用的模块,与我们将描述的新胶水粘合在一起。
(强调我的)
所以回到最初的问题,为了在Javascript中进行FP,你需要通过实现单独的模块来构建程序,然后将它们粘合在一起。 Javascript带有一些不错的胶水,你可以自己构建更多的胶水 - 查看combinators。
您应该尝试避免使用胶水来组合模块化代码段的功能,例如:
while
- 循环作为函数参数传递+
指定给变量或将其作为参数传递(当然,这些问题可以在一定程度上得到缓解 - 查看Javascript FP库的示例。)
另一方面,不是函数的函数 - 例如类,对象,继承,原型等 - 并不一定会阻止你使用Javascript的粘合剂,因此从FP的角度来看没有理由,不要使用它们。