拥抱函数式编程 I - 基本概念
函数编程与命令性编程
为支持使用纯函数方法解决问题,特此创建了函数编程范例。 函数编程是一种声明性编程形式。相比之下,大多数主流语言,包括面向对象的编程 (OOP) 语言(如 C#、Visual Basic、C++ 和 Java–)主要都是为支持命令性(过程性)编程而设计的。
使用命令性方法时,开发人员编写的代码应严格细致地说明计算机为完成目标而必须执行的步骤。 这有时称为算法编程。相比之下,函数方法涉及将问题编成一组要执行的函数。 您要仔细地定义每个函数的输入值和每个函数的返回值。 下表说明了这两种方法之间的一些总体差别。
| 特征 | 命令性方法 | 函数方法 |
|---|---|---|
| 程序员关注点 | 如何执行任务(算法)和如何跟踪状态更改。 | 需要哪些信息和需要进行什么转换。 |
| 状态更改 | 重要。不存在。 | |
| 执行顺序 | 重要。 | 不太重要。 |
| 主要流控制 | 循环、条件和函数(方法)调用。 | 函数调用,包括递归。 |
| 主要操作单元 | 结构或类的实例。 | 作为第一类对象和数据集合的函。 |
虽然多数语言旨在支持特定编程范例,但许多通用语言具有很高的灵活性,能够支持多个范例。 例如,包含函数指针的多数语言都可用于可靠地支持函数编程。 而且,在 C# 3.0 和 Visual Basic 9.0 中,已添加了显式语言扩展以支持函数编程,包括 lambda 表达式和类型推理。 LINQ 技术是一种声明性函数编程形式。
函数式编程与面向对象
在不少的论坛或社区我们不难听见这样的一些声音:函数式编程必将取代面向对象, 面向对象已死。这令到我不得不想起在《代码大全》中看到的这样一番话:
如果百分之一百地相信一种方法论,那么你就只能用一种观点看世界了。
方法学不存在对立,只有互补。一种方法学的发展必然建立于另一种方法学的基础力求补充其不足而得发展并被人认同。
为什么要采用函数式编程
使用函数式编程的最大理由是,使用相同的语言如果以FP方式开发可以:
- 大幅度减少代码量 - 函数式比指令式代码需求更少
- 提幅度提升代码性能 - 函数式是并行运行的
- 提幅度提高稳定性 - 因为状态不变性导致代码并“无副作用”
如果用传统的观点阅读函数式代码会觉得“可读性”极差,“难以理解” 甚至有国外的专家曾引用 Martin fowler 的名句抨击函数式编程:
连傻子都能写出让机器机能运行的代码,但优秀的代码却是能让人理解的。
而所谓的“难以理解”我认为是因为更多的可能性是用过去的习惯的思路去看待函数式编程,那当然“难以理解”,存在即合理如果函数式编程真的不具有生命力,那么各种的开发语言的新版本中就不会不断地为支持函数式编程而引入相关的特性,函数式编程已不是专属于OCaml, Groovy, Scala, Erlang, ML, F#和LISP 这些因函数编程而生的语言了,C#, java , python, ruby 甚至 javascript同样可以使用函数式编程,因为这是一种通用的方法论,一种能改善传统OOP设计和开发中的短板的方法论,因此我们需要它,即使不是马上使用也不应该拒绝了解。
融合而非对立
在我的项目开发中OOP会占据极重要的位置,OOP的方法学极为之擅长于在软件的分析与设计领域,而函数式编程更则重于高效编码。这两种方法学之间本质上并没有互斥性,如同任何一位能驾驭OOP的的设计师能用任何一种非OOP的语言开发出OOP的程序一样,FP只是一种高效编码的思维,只是从由至顶向下的顺序式,有状态化的传统写法中转化为并行式,无状态式的短少精干的代码。他们本来都是干将莫邪,合而为一将无坚不催。
FP可以拟补OOP语言中代码量大,线程不确保安全性能低下等的问题,同时反过来在类中同样OOP也能拟补大量函数的组织与分类的问题。因此在面对函数式编程是在我们的认知中扩展新的方法学,而非更换方法学。
函数式编程的基本概念
以下是函数式编程中的一般性概念:
- 第一类对象/一级对象 (First-class functions)
- 纯函数 (Pure functions)
- 递归 (Recursion)
- 变量的不变性 (Immutable variables)
- 非严格求值 (Nonstrict evaluation)
- 语句 (Statements)
第一类对象
在函数式编程中函数是第一类对象、或称第一类值即所谓的 First-Class functions(正如类是面向对象编程中的第一类对象一样)且要满足下列特征:
- 可以被存放在变量和数据结构中
- 可作为参数传递给子函数
- 可作为子函数的返回值
- 可在运行期间被创建
第一类函数(First-Class functions)是函数式编程的最基本的风格要素,是一个高度抽象的概念,而高阶函数(Higher-order functions)则是一级函数的最佳实践。
我们经常也会把函数对象传递给我们自己定义的函数,不过一般情况下这些自定义的函数就是前文提及的内建函数的某种形式的组合。通过组合使用这三种函数式编程内建的函数, 能够实现范围惊人的“执行流程”操作(全都不用语句,仅仅使用表达式实现)。
纯函数 - Pure Functions
“纯”表示这些函数是“可组合的”,“无副作用的” (No Sides effects)。这要求这些函数具有以下特点:
- 独立的,这样函数就可以自由排序和重新排列,而不会与程序的其他部分相互牵连和依赖。
- 无状态的,因而对相同输入执行相同的函数或特定的一组函数将始终产生相同的输出。
可以简单地理解为:纯函数不会引用或更改函数体作用域以外的任何变量或对象,甚至输入参数,它只负责处理输入->求值->返回输出值。
递归 - Recursion
函数式编程中是采用递归函数或高阶函数取代传统的for或for each语句,避免枚举而不得不违反DRY原则。这里并不是指所有的循环都会采用递归进行处理,因为递归会直接影响程序的运行速度,直正应用中更多会采用“尾递归”对传统递归进行优化。
不变性变量 - Immutable Variables
这是一个挺难理解概念,不变的变量不就是常量吗?其实不然,常量与变量在内存中的存在形式并不一样,常量在程序销毁前是不会释放的,而变量则是跟随作用域释放的。具有不变性的变量就是只一但赋值就不会再修改变量的值,在java或是c#中可以用 final 关键字定义,在swift则使用 let 而不是 var。 这看上去可能是一件不可能的事,如果变量不可被修改,那么变量不就完全失去意义了吗?回顾上文,函数式编程写出的代码面向的是多线程的运行环境,如果变量状态不确定,那么就不能确保代码的线程安全,而出现求值错误、死锁甚至崩溃。然而,先不要以指命式编程的思想去考虑这个如何实现,在后面的章节我会用代码实例说明具有不变性变量的具体实现,现在只需要知道这是FP中的一个重要概念。
非严格求值 - Nonstrict Evaluation
非严格求值又称为惰性求值(Lazy Evaluation),就是指变量的取值不是像指令式编程的方式定义时就赋予初始值,而是在引用时才计算其具体的值,反言之则是如果变量从未被引用则永远不会具有值。
语句 - Statments
函数式编程是通过一小段代码对变量求值或执行单一操作,所以并不需要需要 if、for 一类的流控制语句,取而代之的可以是匿名方法或是ladbma表达式。这也是为什么函数式编程的代码量会比指命式编程的代码少很多的原因之一。因为流程控制语句对FP而然就是对DRY的违反,每一种语句都可以通过函数予以取缔。
结语
由于函数式编程中的概念很多,本文只是从抽像概念上进行了一些介绍。而更多具体的概念如:匿名函数、柯里化、闭包等的具体内容将会在接续的章节中配合原源代码示例一一具体说明 ,下一章将会详细地讲述“第一类函数”的实现,而为了说明得更容易并且能真实地将函数式编程应用到实战中,我会采用 javascript 和 coffeescript为作范例的基准语言 。
拥抱函数式编程 I - 基本概念的更多相关文章
- [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念
本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程? java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...
- [五]java函数式编程归约reduce概念原理 stream reduce方法详解 reduce三个参数的reduce方法如何使用
reduce-归约 看下词典翻译: 好的命名是自解释的 reduce的方法取得就是其中归纳的含义 java8 流相关的操作中,我们把它理解 "累加器",之所以加引号是因为他并不仅仅 ...
- 【大前端攻城狮之路】JavaScript函数式编程
转眼之间已入五月,自己毕业也马上有三年了.大学计算机系的同学大多都在北京混迹,大家为了升职加薪,娶媳妇买房,熬夜加班跟上线,出差pk脑残客户.同学聚会时有不少兄弟已经体重飙升,开始关注13号地铁线上铺 ...
- 函数式编程之柯里化(curry)
函数式编程curry的概念: 只传递给函数一部分参数来调用函数,然后返回一个函数去处理剩下的参数. var add = function(x) { return function(y) { retur ...
- Python 函数式编程 & Python中的高阶函数map reduce filter 和sorted
1. 函数式编程 1)概念 函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念.wiki 我们知道,对象是面向对象的第一型,那么函数式编程也是一样,函数是函数 ...
- 翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 引言&前言
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 译者团队(排名不分先后):阿希.blueken.brucec ...
- 翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 全本 | iKcamp翻译 | 《JavaScript 轻量级函数式编程》|《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson - <You-Dont-Know-JS>作者 译者团队(排名不分先后):阿希.blueken.bruc ...
- 学会JavaScript函数式编程(第1部分)
摘要: JS函数式编程入门. 原文:学会使用函数式编程的程序员(第1部分) 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 在这篇由多部分组成的文章中,接下来将介绍函数式编程的一些概念 ...
随机推荐
- [SQLSERVER] 把TransactionLog截断
注意:以下语句非常危险 --BACKUP LOG MyDb TO DISK=’NUL:’
- Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)
前言 由于在开发My Blog项目时使用了大量的技术整合,针对于部分框架的使用和整合的流程没有做详细的介绍和记录,导致有些朋友用起来有些吃力,因此打算在接下来的时间里做一些基础整合的介绍,当然,可能也 ...
- [Python_7] Python Socket 编程
0. 说明 Python Socket 编程 1. TCP 协议 [TCP Server] 通过 netstat -ano 查看端口是否开启 # -*-coding:utf-8-*- "&q ...
- unbuntu 安装python包提示E: Unable to locate package python-timeout
今天本想着在unbuntu环境下安装python的一个包,安装了几次都提示 E: Unable to locate package python-timeout 查阅了一些信息才知道,原来是一些软件源 ...
- mysql 错误2203 1061 及安装最后出现2003现象的解决办法
错误描述 : 1.#2003-服务器没有响应MySQL无法启动 2.Can''t connect to MySQL server on ''localhost'' (10061) 3.ERROR 20 ...
- Beta阶段总结博客
Beta阶段的规划 1.完善并做完阿尔法版本 2.美化界面 3.规格化GIt的使用 4.新增登录注册功能 5.与服务器端有了交互,实现了前后端的对接 6.增加了一些特效 7.每个人分了模块并且最后对于 ...
- $Gauss$消元
$Gauss$消元 今天金牌爷来问我一个高消的题目,我才想起来忘了学高消... 高斯消元用于解线性方程组,也就是形如: $\left\{\begin{matrix}a_{11}x_1+a_{12}x_ ...
- 【转】PHP中file_put_contents追加和换行
在PHP的一些应用中需要写日志或者记录一些信息,这样的话. 可以使用fopen(),fwrite()以及 fclose()这些进行操作. 也可以简单的使用file_get_contents()和fil ...
- Spark项目之电商用户行为分析大数据平台之(二)CentOS7集群搭建
一.CentOS7集群搭建 1.1 准备3台centos7的虚拟机 IP及主机名规划如下: 192.168.123.110 spark1192.168.123.111 spark2192.168.12 ...
- 数据同步canal客户端
1.增量订阅.消费设计 get/ack/rollback协议介绍: ① Message getWithoutAck(int batchSize),允许指定batchSize,一次可以获取多条,每次返回 ...