swift之函数式编程

时间:2021-11-23 12:45:32

函数式编程初探

最近初学swift,和OC比,发现语言更现代,也有了更多的特性。如何写好swift代码,也许,熟练使用新特性写出更优秀的代码,就是答案。今天先从大的方向谈谈swift中的编程范式-函数式编程。主要还是读了大佬帖子,写写自己的理解。

什么是函数式编程

"函数式编程"是一种"编程范式"(programming paradigm),也就是如何编写程序的方法论。

它属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。

它使代码更像自然语言,告诉程序员要干什么,而不是怎么干,把怎么干的细节拆分到各个函数中。调用的地方逻辑清晰,便于debug。

举例来说,现在有这样一个数学表达式:

  ( + ) *  - 

传统的过程式编程,可能这样写:

var a =  + ;

var b = a * ;

var c = b - ;

函数式编程要求使用函数,我们可以把运算过程定义为不同的函数,然后写成下面这样:

var result = subtract(multiply(add(,), ), );

这就是函数式编程。

为什么使用函数式编程

  1. 代码简洁,开发快速

    函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。

  2. 接近自然语言,易于理解

    函数式编程的*度很高,可以写出很接近自然语言的代码。我们把程序的逻辑分成了几个函数,这样一来,我们的代码逻辑也会变得几个小碎片,于是我们读代码时要考虑的上下文就少了很多,阅读代码也会更容易。 而把代码逻辑封装成了函数后,我们就相当于给每个相对独立的程序逻辑取了个名字,于是代码成了自解释的。

    前文曾经将表达式(1 + 2) * 3 - 4,写成函数式语言:

    subtract(multiply(add(,), ), )

    对它进行变形,不难得到另一种写法:

      add(,).multiply().subtract()
  3. 更方便的代码管理

    函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。

  4. 易于"并发编程"

    函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。

函数式编程的一些重要概念

函数是"第一等公民"

所谓"第一等公民"(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

举例来说,下面代码中的print变量就是一个函数,可以作为另一个函数的参数。

var print = function(i){ console.log(i);};

[,,].forEach(print);

函数(function)和procedure是有区别的

函数(function)这个名词来自于数学,函数通过一个给定的值,计算出另外一个值,也就是上学时常见的f(x)。

在通常的理解中,下面代码里面,f1,f2,f3通常都被叫做函数:

//伪代码 函数无入参,返回2
def f1(): return
//函数有参数x,返回 x+1
def f2(int x): return x+
//函数无入参,无返回值,打印hello world
def f3(): print("hello world")

复制代码但实际上,函数(function)和procedure是有区别的: function 通过运算返回一个值,而procedure只执行一段代码,没有返回值。 这一点对于后面的理解是非常有帮助的,首先要区分出二者。

再回到上面的代码中,f1,f2,为function而f3为procedure。

Pure Function(纯函数)

Pure:纯的; 单纯的; 纯真的; 干净的 我们将满足下面两个条件的函数称作Pure Function:

  • 函数不会产生side effect(no side effect)
  • 函数满足referential transparency这个条件 (原谅我不会翻译这两个名词)

  1. Side effect

    函数调用后不会对外部状态产生影响,比如下面这段代码中sum函数是no side effect的:

    def sum(a,b): return a+b

    产生side effect的函数长成什么样呢?其实经常会写这样的函数:

    int sum =
    def plus(a,b){
    sum = a + b
    return sum
    }

    plus函数除了计算参数之和以外,还改变了外部变量sum的值,我们plus这个函数产生了side effect。 常见的Side effect

  • 改变外部变量的值(上面的例子中plus函数)
  • 像磁盘中写入数据
  • 将页面上的一个按钮设置为可点击,或者不可点击

    前面提到function和procedure的不同点,在side effect这个角度来讲,pure funcion不会产生side effect,procedure通常会产生side effect。

    满足Referential Transparency的函数可以将可以将用函数计算的结果替换表达式本身,而不影响程序的逻辑。 给定指定的参数,在任何时候返回的值都是相同的。不受其他外部条件影响。

    两者说的意思是一样的,只是表达的角度是不同的 举个满足RT的例子

    def f(): return 
    
    print(f() + f())
    print()

    下面这段代码中的f()是满足RT的函数,按照上面的解释,我们可以将f()的结果也就是2替换掉f(),不会影响程序本身的逻辑:

    def f(): return 
    
    print( + f())
    print()

    或者这样替换:

    def f(): return 
    
    print(f() + )
    print()

    从另一个角度说,f()这个函数无论在什么时候调用,返回的值都是一样的,不会发生改变(没有外部条件影响)

    举个不满足RT的例子

    int counter = 
    
    def f(x){
    counter +=
    return x + counter
    }

    这个例子中,f(x)这个函数不满足RT 下面的代码中,当我们用f(1)的计算结果一次替换代码中f(1)本身时,程序的逻辑是错误的:

    //原始的代码执行结果是:3
    f() + f() //把f(1)的结果1替换进来,下面函数执行的结果是:2
    f() + //同样,得到2
    + f() //得到2
    +

    我们不能用执行的结果替换函数本身, 换个角度,下面两行代码执行的结果也不同

    f() + f()
    * f()

    虽然入参都为1,但f(1)在不同时候调用得到的结果不同,因此f不满足RT这个条件

    回到pure function,理解了side effect 和 referential transparency的含义,我们再来重温pure function的定义,就很好理解了:

  • No side effect

  • Referential transparency

    满足这两个条件的函数,称之为pure function

引用透明

引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。

有了前面的第三点和第四点,这点是很显然的。其他类型的语言,函数的返回值往往与系统状态有关,不同的状态之下,返回值是不一样的。这就叫"引用不透明",很不利于观察和理解程序的行为。

swift中函数式编程的应用

高阶函数

先说两个概念型的名词:

高阶函数(high order func),指可以将其他函数作为参数或者返回结果的函数。

一级函数(first class func),指可以出现在任何其他构件(比如变量)地方的函数。

map

map { (Element) -> Element in
对 element 进行处理
}

一般用在集合类型,对集合里的元素进行遍历,函数体里实现对每一个元素的操作。

var arr = [,,,]
let mapres = arr.map {
"NO." + String($)
}

// 运行结果:["NO.1", "NO.3", "NO.2", "NO.4"]

reduce

reduce(Result) { (Result, Element) -> Result in
基于 Result 对当前的 Element 进行操作,并返回新的 Result
}

一般用在集合类型,对集合里的元素进行叠加处理,函数体里传两个参数,第一个是之前的叠加结果,第二个是当前元素,返回值是对当前元素叠加后的结果。

// 对数组里的元素:奇数相加,偶数相乘
var arr = [,,,]
let reduceRes = arr.reduce((,)) { (a:(Int,Int), t:Int) -> (Int,Int) in
if t % == {
return (a. + t, a.)
} else {
return (a., a. * t)
}
}
// 运行结果:(4,8)

filter

filter { (Element) -> Bool
对元素的筛选条件,返回 Bool
}

一般用在集合类型,对集合里的元素进行筛选。函数体里实现筛选条件,返回 true 的元素通过筛选。

var arr = [,,,]
let filterRes = arr.filter {
$ % ==
}
// 运行结果:[2,4]

flatMap

首先先看下 Swift 源码里对集合数组的map和flatmap的实现:

// Sequence.swift
extension Sequence {
public func map<T>(_ transform: (Element) -> T) -> [T] {}
} // SequenceAlgorithms.swift.gyb
extension Sequence {
public func flatMap<T>(_ transform: (Element) -> T?) -> [T] {}
public func flatMap<S : Sequence>(_ transform: (Element) -> S) -> [S.Element] {}
}

前面我们已经知道,map是一种遍历,而上面的代码又显示出来,flatmap有两种重载的函数:

其中一种与map非常相似,差别只在闭包里的返回值变成了可选类型。 另一种稍微有点不同,闭包传入的是数组,最后返回的是数组的元素组成的集合。

// map
let arr = [,,nil,,nil,]
let arrRes = arr.map { $ } // 结果为:[Optional(1), Optional(2), nil, Optional(4), nil, Optional(5)] // flatmap
let brr = [,,nil,,nil,]
let brrRes = brr.flatmap { $ } // 结果为:[1, 2, 4, 5] let crr = [[,,],[,,]]
let ccRes = crr.flatmap { $ } // 结果为:[1, 2, 4, 5, 3, 2]
let cdRes = crr.flatmap { c in
c.map { $ * $ }
} // 结果为[1, 4, 16, 25, 9, 4] // 使用 map 实现的上面平铺功能
let ceRes = Array(crr.map{ $ }.joined()) // 同 ccRes
let cfRes = Array(crr.map{ $ }.joined()).map{ $ * $ } // 同 cdRes

简单理解为,flatMap可以将多维数组平铺,也还以过滤掉一维数组中的nil元素。

map和flatMap不只在数组中可以使用,对于 Optional 类型也是可以进行操作的。先看下面这个例子:

let a: Date? = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium let c = a.map(formatter.string(from:))
let d = a == nil ? nil : formatter.string(from: a!)
c 和 d 是两种不同的写法,c 写法是不是更优雅一些?

参考文章:

理解函数式编程的本质

函数式编程初探

swift函数式编程介绍

Swift之函数式编程

swift之函数式编程的更多相关文章

  1. swift 之函数式编程(一)

    1. 什么是函数式编程? 函数式编程是阿隆佐思想的在现实世界中的实现, 它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及异变物件. 函数式编程的最重要基础是λ演算.而且λ演算的函數可以接受函 ...

  2. swift之函数式编程(四)

    文章内容来自<Functional Programing in Swift>,具体内容请到书中查阅 Map, Filter, Reduce Functions that take func ...

  3. swift之函数式编程&lpar;二&rpar;

    本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...

  4. 最通俗易懂的方式让你理解 Swift 的函数式编程

    函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心.善用函数式编程思路,可以对我们的开发工作有很大的帮助和 ...

  5. swift之函数式编程(三)

    文章来源于<Functional Programing in Swift>,本系列仅仅是观后概括的一些内容 Wrapping Core Image 上一篇文章我们介绍了 高阶函数并且展示了 ...

  6. swift之函数式编程&lpar;五&rpar;

    文章内容来源于<Functional Programing in Swift>,详情请看原著 The Value of Immutability swift 对于控制值改变有一些机制.在这 ...

  7. Swift の 函数式编程

    Swift 相比原先的 Objective-C 最重要的优点之一,就是对函数式编程提供了更好的支持. Swift 提供了更多的语法糖和一些新特性来增强函数式编程的能力,本文就在这方面进行一些讨论. S ...

  8. swift语言的特征:类型系统与函数式编程&colon;swift是面向类型和面向函数编程的语言

    swift语言的特征: 类型系统:值类型与引用类型.泛型.协议类型 函数式编程:

  9. 函数式编程-将Monad(单子)融入Swift

    前言 近期又开始折腾起Haskell,掉进这个深坑恐怕很难再爬上来了.在不断深入了解Haskell的各种概念以及使用它们去解决实际问题的时候,我会试想着将这些概念移植到Swift中.函数式编程范式的很 ...

随机推荐

  1. oracle一点记录

    查看数据库实例名(SERVICE_NAME): sql: select instance_name from v$instance; 如何知道oracle客户端是32位还是64的.windows下启动 ...

  2. 20145330孙文馨 《Java程序设计》第二周学习总结

    20145330孙文馨第二周学习总结 第二周相比于第一周对java语言有了深一点的了解,也意识到多敲代码才是学习计算机语言的最好方法. 教材内容总结 类型.变量与运算符 *基本类型 整数(short. ...

  3. hello,world!

    这可以算是我的第一篇博客了吧?真正意义上的开始... 我于2016年6月毕业于山东英才学院信工学院计算机管理系,至今已有近半年.几经周转,总算是准备稳定下来了.于是,当初的博客想法提上了日程. 本人工 ...

  4. 通往成功的钥匙--Web前端开发技术

    互联网是一个服务性行业,用户对网站良好的体验度,直接影响到网站的效果.无论你做了多少广告推广,没有用户体验度等于零.Web前端技术是为了解决用户体验度而诞生的.无论是百度.新浪.阿里巴巴等大型网站,还 ...

  5. 【原创】QT编程 多线程

    请先保证已安装QT,没有请参考 http://www.cnblogs.com/kavs/p/4608926.html  安装QT. 新建threads文件夹存放项目:mkdir threads sud ...

  6. iOS中webView加载URL需要处理特殊字符

    今天在项目中遇到webView加载URL时,因为URL中有特殊字符,导致页面无法加载,而且在- (BOOL)webView:(UIWebView )webView shouldStartLoadWit ...

  7. Hadoop集群时间同步

    0x01 ntp安装 检查时间服务器是否安装 # rpm -q ntp ntp-4.2.4p8-2.el6.x86_64 // 这表示已安装了,如果没有安装,这是空白. //如果没有安装,我们按照下 ...

  8. iOS TabelViewCell 删除 编辑 插入

    /** TableView 进入或退出编辑状态(TableView 方法). */ - (void)setEditing:(BOOL)editing animated:(BOOL)animate{ / ...

  9. matplotlib 入门之Usage Guide

    文章目录 Usage Guide plotting函数的输入 matplotlib, pyplot, pylab, 三者的联系 Coding style Backends 后端 matplotlib教 ...

  10. JDBC事务,银行转账,货物进出库等等。

    1:转账业务 转账必须执行2个sql语句(update更新)都成功的情况下,提交事务,如果有一个失败,则2个都回滚事务2:事务应该具有4个属性:原子性.一致性.隔离性.持久性.这四个属性通常称为ACI ...