拥抱函数式编程 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经授权转载,版权归原作者所有. 在这篇由多部分组成的文章中,接下来将介绍函数式编程的一些概念 ...
随机推荐
- Mysql binlog 无法删除(purge命令无法删除)
1.版本 1)操作系统 cat /etc/issueCentOS release 6.6 (Final)Kernel \r on an \m cat /proc/versionLinux versio ...
- Ctrl+Alt+F1~F6
Ctrl+ALT+F1~F6 可以进入不同的字符终端和图形界面.体现了 linux 或者 unix 的多用户的特点. 6个不同的终端,相当于六个不同的用户. 保持更新,转载请著名出处.
- orcle 如何快速插入百万千万条数据
有时候做实验测试数据用到大量数据时可以用以下方法插入: 方法一:使用xmltable create table bqh8 as select rownum as id from xmltable('1 ...
- sql点滴—mysql中查询表的信息
mysql中查询表的信息 查询mysql表字段信息的sql语句 SHOW DATABASES //列出 MySQL Server 数据库. SHOW TABLES [FROM db_name] //列 ...
- from urllib.request import urlopen
from urllib.request impor urlopen (负责打开浏览url内的html 文本) re.compile(r'alex(?P<name>\d+)and' ...
- 【CSS】Sass理解
原文在 https://github.com/zhongxia245/blog , 欢迎 star! Sass理解 时间:2016-09-24 22:56:12 作者:zhongxia 这里就不讲解S ...
- 团队作业——Alpha冲刺 7/12
团队作业--Alpha冲刺 冲刺任务安排 杨光海天 今日任务:将编辑界面与标题栏合并.与同队成员,讨论部分功能合并的问题. 明日任务:编辑界面与另一队员完成的字体设置弹窗合并. 郭剑南 今日任务:使用 ...
- vue2.0学习笔记之路由(二)路由嵌套
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- runloop是iOS系统上的actor模式
runloop是iOS系统上的actor模式(单线程派发的)
- Codeforces Round #503 Div. 2
时间相对来说还是比较合适的,正好放假就可以打一打啦. A. New Building for SIS:http://codeforces.com/contest/1020/problem/A 题意概述 ...