学习函数式编程的第一步也是最重要的一步就是知道它是什么,做什么用的。学开车我们刚开始学开车时,很吃力。当我们看到别人在做这件事的时候,看起来确实很容易。但结果比我们想象的要难。我们在父母的车里练习,直到熟悉了附近的街道,我们才敢在公路上冒险。但是经过反复的练习,我们学会了开车,最终拿到了驾照。有了执照,我们一有机会就把车开出去。每次短暂旅行,我们驾车技术变得越来越好,随之我们的信心逐渐上升。或许有那么一天我们不得不开别人的车或者我们的老车就报废了不得不买一辆新的时候。第一次驾驶一辆不同的车是什么感觉? 就像第一次开车一样吗? 第一次,一切都那么陌生。我们之前坐过车,但只是作为乘客。这一次我们掌握了主动权可以自己开车。但是当我们开第二辆车的时候,我们只是自问几个简单的问题,比如,插钥匙口去在哪个位置,灯在哪里,怎么使用转弯信号,怎么调整侧视镜等等。从那以后,一切都很顺利。但是为什么这次和第一次相比如此容易呢?那是因为新车和旧车很像。它拥有汽车所需要的所有基本的东西,而且它们几乎在同一个地方。新车与旧车有些东西的构造不同,可能还有一些额外的功能,但我们第一次开车甚至第二次都没有使用它们。 但是,最终我们还是学会我们关心的那些内容。所以学习编程语言也是类似学开车一样。第一次是最难的,但一旦你有了第一次经验,后续的就会更容易。当你刚开始学习第二门语言时,你会问这样的问题,“我如何创建一个模块? 如何搜索数组? substring 函数的参数是什么? “你相信你能学会驾驶这门新语言,因为它会让你想起你的旧语言,也许有一些新东西可以让你的生活更轻松。如果你要驾驶宇宙飞船,你不会期望你在路上的驾驶能力会对你有很大的帮助。你的第一个宇宙飞船不管你一辈子开的是一辆车还是几十辆车,想象一下你即将驾驶一艘宇宙飞船。在开始训练的时候,你会预想太空中的场景与地面不现,宇宙飞船架使方式与汽车的驾驶方式也是不同的。但是物理世界没有改变,就像你在同一个宇宙中遨游一样。这和学习函数式编程是一样的。你应该预想到代码风格与你之前所编写不太一样,思路也是有很大差异的。非函数式编程与函数式编程的思维模式大相径庭。当你学会了,你会喜欢上它以至于你可能永远不会回到原有旧思维模式。忘记你所知道的一切人们喜欢说这句话,但这有点真实。 学习函数式编程就像从头开始。有很多相似的概念,但如果你只是希望你必须重新学习所有东西,那就最好了。当你有了想要学习函数式编程的欲望,有会有学习动力,有了动力,当事情变得困难时,你就不会放弃。作为一名程序员,有很多事情是你习惯做的,但是你不能再用函数式编程来做了。就像在你的车里一样,你过去常常倒车离开车道。但是在宇宙飞船里,没有倒车。现在你可能会想,“什么? 没有反向? 我怎么能不倒车呢?”事实证明,在宇宙飞船里你不需要倒车因为它在三维空间里的推动能力。一旦你明白了这一点,你就再也不会想要倒车了。事实上,有一天,你会回想起汽车的局限性。学习函数式编程需要一段时间,所以要有耐心。因此,让我们离开命令式编程这个冰冷的世界,来温习一下函数式编程的温泉。在这篇由多部分组成的文章中,接下来将介绍函数式编程的一些概念,这些概念对你学习函数式编程有所帮助。如果你已经懂了什么是函数式编程,这可以加深你的理解。请不要着急。从这一点开始,花点时间阅读并理解代码示例。你甚至可能想在每节课结束后停止阅读,以便让你的观点深入理解,然后再回来完成。最重要的是你要理解。纯函数(Purity)所谓纯函数,就是指这样一个函数,对于相同的输入,永远得到相同的输出,它不依赖外部环境,也不会改变外部环境。如果不满足以上几个条件那就是非纯函数。下面是Javascript中的一个纯函数示例:var z = 10;function add(x, y) { return x + y;}注意,add 函数不涉及z变量。它不从z读取,也不从z写入,它只读取x和y,然后返回它们相加的结果。这是一个纯函数。如果 add 函数确实访问了变量z,那么它就不再是纯函数了。请思考一下下面这个函数:function justTen() { return 10;}如果函数justTen是纯的,那么它只能返回一个常量, 为什么?因为我们没有给它任何参数。 而且,既然是纯函数的,除了自己的输入之外它不能访问任何东西,它唯一可以返回的就是常量。由于不带参数的纯函数不起作用,所以它们不是很有用。所以justTen被定义为一个常数会更好。大多数有用的纯函数必须至少带一个参数。考虑一下这个函数:function addNoReturn(x, y) { var z = x + y}注意这个函数是不返回任何值。它只是把变量x和y相加赋给变量z,但并没有返回。这个也是一个纯函数,因为它只处理输入。它确实对输入的变量进行操作,但是由于它不返回结果,所以它是无用的。所有有用的纯函数都必须返回一些我们期望的结果。让我们再次考虑第一个add函数:注意 add(1, 2) 的返回结果总是 3。这不是奇怪的事情,只是因为 add 函数是纯的。如果 add 函数使用了一些外部值,那么你永远无法预测它的行为。在给定相同输入的情况下,纯函数总是返回相同的结果。由于纯函数不能改变任何外部变量,所以下面的函数都不是纯函数:writeFile(fileName);updateDatabaseTable(sqlCmd); sendAjaxRequest(ajaxRequest);openSocket(ipAddress);所有这些功能都有副作用。当你调用它们时,它们会更改文件和数据库表、将数据发送到服务器或调用操作系统以获取套接字。它们不仅对输入操作同时也对输出进行操作,因此,你永远无法预测这些函数将返回什么。纯函数没有副作用。在Javascript、Java 和 c# 等命令式编程语言中,副作用无处不在。这使得调试非常困难,因为变量可以在程序的任何地方更改。所以,当你有一个错误,因为一个变量在错误的时间被更改为错误的值,这不是很好。此时,你可能会想,“我怎么可能只使用纯函数呢?”函数式编程不能消除副作用,只能限制副作用。由于程序必须与真实环境相连接,所以每个程序的某些部分肯定是不纯的。函数式编程的目标是尽量写更多的纯函数,并将其与程序的其他部分隔离开来。不可变性 (Immutability)你还记得你第一次看到下面的代码是什么时候吗?var x = 1;x = x + 1;教你初中数学的老师看到以上代码,可能会问你,你忘记我给你教的数学了吗? 因为在数学中,x 永远不能等于x + 1。但在命令式编程中,它的意思是,取x的当前值加1,然后把结果放回x中。在函数式编程中,x = x + 1是非法的。所以这里你可以用数学的逻辑还记得在数式编程中这样写是不对的!函数式编程中没有变量。由于历史原因,存储值的变量仍然被称为变量,但它们是常量,也就是说,一旦x取值,这个常量就是x返回的值。别担心,x 通常是一个局部变量,所以它的生命周期通常很短。但只要它还没被销毁,它的值就永远不会改变。下面是Elm中的常量变量示例,Elm是一种用于Web开发的纯函数式编程语言:addOneToSum y z = let x = 1 in x + y + z如果你不熟悉ml风格的语法,让我解释一下。addOneToSum 是一个函数,有两个参数分别为y和z。在let块中,x被绑定到1的值上,也就是说,它在函数的生命周期内都等于1。当函数退出时,它的生命周期结束,或者更准确地说,当let块被求值时,它的生命周期就结束了。在in块中,计算可以包含在let块中定义的值,即 x,返回计算结果 x + y + z,或者更准确地说,返回 1 + y + z,因为 x = 1。你可能又会想 :“我怎么能在没有变量的情况下做任何事情呢?”我们想一下什么时候需要修改变量。通常会想到两种情况:多值更改(例如修改或记录对象中的单个值)和单值更改(例如循环计数器)。函数式编程使用参数保存状态,最好的例子就是递归。是的,是没有循环。“什么没有变量,现在又没有循环? ”我讨厌你! ! !”哈哈,这并不是说我们不能做循环,只是没有特定的循环结构,比如for, while, do, repeat等等。函数式编程使用递归进行循环。这里有两种方法可以在Javascript中执行循环:注意,递归是一种函数式方法,它通过使用一个结束条件 start (start + 1) 和调用自己 accumulator (acc + start) 来实现与 for 循环相同的功能。它不会修改旧的值。相反,它使用从旧值计算的新值。不幸的是,这在 Javascript中 很难想懂,需要你花点时间研究它,原因有二。第一,Javascript的语法相对其它高级语言比较乱,其次,你可能还不习惯递归思维。在Elm,它更容易阅读,如下:sumRange start end acc = if start > end then acc else sumRange (start + 1) end (acc + start) 它是这样运行的:你可能认为 for 循环更容易理解。虽然这是有争议的,而且更可能是一个熟悉的问题,但非递归循环需要可变性,这是不好的。在这里,我还没有完全解释不变性的好处,但是请查看全局可变状态部分,即为什么程序员需要限制来了解更多。我还没有完全解释不可变性(Immutability)在这里的好处,但请查看 为什么程序员需要限制的全局可变状态部分 以了解更多信息。不可变性的好处是,你读取访问程序中的某个值,但只有读权限的,这意味着不用害怕其他人更改该值使自己读取到的值是错误。不可变性的还有一个好处是,如果你的程序是多线程的,那么就没有其他线程可以更改你线程中的值,因为该值是不可变,所以另一个线程想要更改它,它只能从旧线程创建一个新值。不变性可以创建更简单、更安全的代码。在本文的后续部分中,将讨论高阶函数、函数组合、局部套用等等,尽请期待!原文:https://medium.com/@cscalfani…编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具Fundebug。你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》!