谈谈函数式编程

函数式编程 ( Functional Programming ) 是一种以函数为基础的编程方式和代码组织方式,能够带来更好的代码调试及项目维护的优势。本篇主要结合笔者在实际项目开发中的一些应用,简要谈谈函数式编程。

函数

在函数式编程中,任何代码可以都是函数,且要求具有返回值,如下示例

// 非函数式
var title = "Functional Programming";
var saying = "This is not";
console.log(saying + title); // => This is not Functional Programming
// 函数式
var say = title => "This is " + title;
var text = say("Functional Programming"); // => This is Functional Programming
纯```
函数

纯函数在这里指函数内外间是“无”关联的。主要有下面两点

没有副作用(side effect)
不会涉及到外部变量的使用或修改

引用透明
函数内只会依赖传入参数,在任何时候对函数输入相同的参数时,总能输出相同的结果

// 非纯函数(函数内依赖函数外的变量值)
var title = "Functional Programming";
var say = ()=> "This is not" + title; // <= 依赖了全局变量 title
// 纯函数
var say = (title)=>"This is " + title; // <= 依赖了以参数 title 传入
say("Functional Programming");

不可变数据(immutable)

这里主要是指变量值的不可变。当需要基于原变量值改变时,可通过产生新的变量来确保原变量的不变性,如下

// 可变数据
var arr = ["Functional", "Programming"];
arr[0] = "Other"; // <= 修改了arr[0]的值
console.log(arr) // => ["Other", "Programming"] // 变量arr值已经被修改
// 不可变数据
var arr = ["Functional", "Programming"];// 得到新的变量,不修改了原来的值
var newArr = arr.map(item => {

if(item === "Functional"){
     return "Other";
} else {
    return item;
}

})
console.log(arr); // => ["Functional", "Programming"] 变量arr值不变

console.log(newArr); // => ["Other", "Programming"]  产生新的变量newArr
之所以使用这种不变值,除了更好的函数式编程外,还能够维持线程安全可靠,落地在业务中,实际上也能让代码更加清晰。
设想,如果你定义了一个变量A,A在其他地方被其他人修改了,这样是不方便定位A的当前值的。关于定义多个变量引发的内存等问题,可以通过重用结构或部分引用的方式来减轻,可参考 immutable.js

使用 map, reduce 等数据处理函数

强大的 JavaScript 有着越来越多的高能处理数据函数,其中包含了 map、 reduce、 filter 等。

map 能够对原数组中的值进行逐个处理并产生新的数组,一个简单例子

// map
var data = [1, 2, 3];
var squares = data.map( (item, index, array) => item * item );
console.log(squares); // => [1, 4, 9]
console.log(data);// => [1, 2, 3] data 还是那个 data

reduce 能够对原数组中的各个值进行结合处理,来产生新的值,如下面例子中,previous 代表上一个值,current 代表当前值,reduce 函数可以传入第二个参数作为 previous 初始值,不传时则 previous 初始值为数组中第一个值。

// reduce
var sum = [1, 2, 3].reduce( (previous, current, index, array) => previous + current );
console.log(sum); // => 6

函数柯里化 Currying

柯里化 是将多参函数转换成一系列的单参函数。结合下面例子来说明下

// 一个多参函数
var add = (a, b) => a + b;
add(1, 2); // => 3
将上面的多参函数进行柯里化,如下

// 柯里化函数
var add = a => b => a + b;

上面柯里化后的函数调用方式也有所转变,第一次传入一个参数返回了一个函数,再传入参数则完成整体的调用,这也是利用的闭包的特性

var add1 = add(1);
add1(2); // => 3

柯里化后的函数,也可以应用在生产 “ 函数 ” 上,如下示例

var say = title => type => title + " is " + type;

var sayFP = say("Functional Programming");
var sayOther = say("Other Programming");

sayFP("good"); // => Functional Programming is good
sayOther("good"); // => Other Programming is good

组合函数 compose

顾名思义,组合函数是将多个函数进行组合成一个函数。举个例子

var compose = (fn1, fn2) => (arg) => fn1(fn2(arg));
var a = arg => arg + 'a';
var b = arg => arg + 'b';
var c = compose(a, b); // 将a,b函数进行组合

c('c'); // => cba

上面示例中,当调用组合函数 c 时,传入的参数会经过 b 函数,接着将 b 函数的返回值作为 a 函数的参数值,从而输出最终结果。
组合函数 c 就像管道一样,将水流( 返回值 )流经各个函数中进行处理。

当想要组合很多函数成一条很长很长的“管道”时,那么显然上面的 compose 函数已经不够用了。下面看看 redux 是怎么做这个 compose 工具函数的。

// 源自: redux/src/compose.js
export default function compose(...funcs) {

if (funcs.length === 0) {
    return arg => arg
  } else {
    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))

}
}

代码很简洁,主要利用了递归方式和数组的 reduceRight 方法来处理,reduceRight 跟上边提到的 reduce 方法功能是一样的,不同的是 reduceRight 是从数组的末尾向前逐个处理。就这样,想拼多长的就多长。

以上,便是笔者在项目实践中应用较多的函数式编程内容,如有不妥,请斧正。
文章转载自 开源中国社区 [http://www.oschina.net]
时间: 2024-09-22 17:19:00

谈谈函数式编程的相关文章

轻量函数式 JavaScript:一、为什么要进行函数式编程?

函数式程序员:(名词)那些将变量命名为"x",函数命名为"f",并将代码模式称为"zygohistomorphic prepromorphism"的程序员. James Iry ‏@jamesiry 5/13/15 https://twitter.com/jamesiry/status/598547781515485184 函数式编程(FP)无论如何都不是一个新概念.它在编程的整个历史中一直存在着.然而可以确定的是 -- 我不确信这样说是否合理,

JavaScript的函数式编程基础指南_基础知识

引言 JavaScript是一种强大的,却被误解的编程语言.一些人喜欢说它是一个面向对象的编程语言,或者它是一个函数式编程语言.另外一些人喜欢说,它不是一个面向对象的编程语言,或者它不是一个函数式编程语言.还有人认为它兼具面向对象语言和函数式语言的特点,或者,认为它既不是面向对象的也不是函数式的,好吧,让我们先搁置那些争论. 让我们假设我们共有这样的一个使命:在JavaScript语言所允许的范围内,尽可能多的使用函数式编程的原则来编写程序. 首先,我们需要清理下脑子里那些关于函数式编程的错误观

F#探险之旅(二):函数式编程(上)

函数式编程范式简介 F#主要支持三种编程范式:函数式编程(Functional Programming,FP).命令式编程(Imperative Programming)和面向对象(Object-Oriented,OO)的编程.回顾它们的历史,FP是最早的一种范式,第一种FP语言是IPL,产生于1955年,大约在Fortran一年之前.第二种FP语言是Lisp,产生于1958,早于Cobol一年.Fortan和Cobol都是命令式编程语言,它们在科学和商业领域的迅速成功使得命令式编程在30多年的

Guava 是个风火轮之函数式编程(1)

前言 函数式编程是一种历久弥新的编程范式,比起命令式编程,它更加关注程序的执行结果而不是执行过程.Guava 做了一些很棒的工作,搭建了在 Java 中模拟函数式编程的基础设施,让我们不用多费手脚就能享受部分函数式编程带来的便利. Java 始终是一个面向对象(命令式)的语言,在我们使用函数式编程这种黑魔法之前,需要确认:同样的功能,使用函数式编程来实现,能否在健壮性和可维护性上,超过使用面向对象(命令式)编程的实现? Function Function 接口是我们第一个介绍的 Guava 函数

基于范型的java函数式编程(一)

编程|函数 注:在您阅读本篇的时候,希望你对Java Generic(范型)能够有所了解和明白. 记:周末在给javaparty讲FP中,很多人似乎对fp并不关心,也认为java中fp的作用不大.其实这是个很大的观念错误,范型的发展,对java的函数式编程支持很大,对Functor的影响也非常大.Functor在算法.逻辑.条件计算.规则引擎等等方面,都会有很大的作为,这个影响可就会深远的多了.-- 估且以此篇的开端,唤醒java开发者对FP in Java的重新认识. 周六给javaparty

F#简明教程一:F#与函数式编程概述

F#是微软.NET开发平台的一门编程语言,其最大的特点是对函数式编程(FP,FunctionalProgramming)的引入:F#对面向对象(OOP)编程的支持也很出色,使用F#语言,开发人员可以自由选择函数式编程或面向对象编程来实现他们的项目.此外,F#还可以与.NET平台上C#.VB等其他编程语言紧密结合. CPU多核心化和云计算的背景下,函数式编程可以很好的解决多并发运算的问题(在处理并发问题方面,面向对象编程存在一定程度的固有缺陷,比如类和实例化过程中产生的一些副作用,详细请参考51C

F#入门:使用.NET Framework中的函数式编程技术

本文讨论: 安装 F# F# 语言基础 .NET 互操作性 异步 F# 本文使用了以下技术: .NET Framework, F# 目录 为什么要使用 F#? 安装 F# 您好,F# Let 表达式 关键字 For 管道 F# 也能够处理对象 异步 F# 与 F# 合作 作 为 Microsoft .NET Framework 家族的新成员,F# 提供类型安全.性能以及类似脚本语言的工作能力,所有这些都是 .NET 环境的一部分.此函数式语言由 Microsoft 研究院的 Don Syme 发

浅谈Java 8的函数式编程

关于"Java 8为Java带来了函数式编程"已经有了很多讨论,但这句话的真正意义是什么? 本文将讨论函数式,它对一种语言或编程方式意味着什么.在回答"Java 8的函数式编程怎么样"之前,我们先看看Java的演变,特别是它的类型系统,我们将看到Java 8的新特性,特别是Lambda表达式如何改变Java的风景,并提供函数式编程风格的主要优势. 函数式编程语言是什么? 函数式编程语言的核心是它以处理数据的方式处理代码.这意味着函数应该是第一等级(First-cla

Python中的函数式编程

虽然人们总把Python当作过程化的,面向对象的语言,但是他实际上包含了函数化编程中,你需要的任何东西.这篇文章主要讨论函数化编程的一般概念,并说明用Python来函数化编程的技术. 我们最好从艰难的问题开始出发:"到底什么是函数化编程呢?"其中一个答案可能是这样的,函数化编程就是你在使用Lisp这样的语言时所做的(还有Scheme,Haskell,ML,OCAML,Mercury,Erlang和其他一些语言).这是一个保险的回答,但是它解释得并不清晰.不幸的是对于什么是函数化编程,很