Swift函数式编程

什么是函数式编程

我们知道面向过程编程,面向对象编程,面向协议编程...大家听说过面向函数编程吗?

函数式编程是种编程范式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是 λ 演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里,函数的计算可随时调用。

Swift函数式编程 - 函数

  • swift语言中的闭包是一段实现独立功能的代码块,和Object-C中的blocks以及其他语言中的lambda表达类似。swift的全局函数是一种特殊形式的闭包.

高阶函数

  • 我们知道如果我们需要在一个类中调用另外一个类中的函数就要使用这个类作为载体来携带函数。现在我们的函数可以传体函数而不需要类作为载体。函数可以作为参数,返回值。

一等函数

在面向对象编程中类作为一等公民,在面向函数式编程中函数作为一等公民。

函数柯里化

  • 函数柯里化是将接受多个参数的函数变成接受单一参数然后返回一个接受余下参数的新函数

函数式思维

  • 例子一 从1到10中找到数组里的奇数
func filterOddArray() -> [Int] {
    var oddArray: [Int] = []
    for i in 0...10 {
        if i % 2 == 1 {
            oddArray.append(i)
        }
    }
    return oddArray
}

print(filterOddArray())

//打印结果 [1, 3, 5, 7, 9]
  • 函数式思维解决,swift已经为我们实现了一些函数来处理这些问题。
let array = [1,2,3,4,5,6,7,8,9,0]
var oddArray = array.filter{$0 % 2 == 1}
print(oddArray)
//打印结果 [1, 3, 5, 7, 9]
  • 我们想来自己实现一个filter函数。我们先看一下它的声明
public func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
  • 我们先实现一个简单版的filter函数,没有错误处理。
extension Array {
    func myFilter(_ isIncluded: (Element) -> Bool) -> [Element] {
        var filterArray: [Element] = []
        for element in self {
            if isIncluded(element) {
                filterArray.append(element)
            }
        }
        return filterArray
    }
}
  • 试试能不能work
let array = [1,2,3,4,5,6,7,8,9,0]
var oddArray = array.myFilter{$0 % 2 == 1}
print(oddArray)
//打印结果 [1, 3, 5, 7, 9]
  • 函数式的写法更为简单的原因是放弃了对循环的控制。而使用了函数处理序列。如何处理序列,及循环体里应该写的代码,在函数编程中是由一个函数传入。在计算机底层对语言的实现中,还是使用了循环控制的概念。就如我们写的那个filter函数。

  • 功能升级,现在我们是求1到10中所有基数的和。直接看如何通过函数式思维来解决吧。

let array = [1,2,3,4,5,6,7,8,9,0]
let sum = array.myFilter{$0 % 2 == 1}.reduce(0) { (total, num) in total + num
}
print(sum)
//打印结果 25
  • 我们实现一下自己的reduce,先看一下定义
public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
  • 来看看实现
extension Array {
    func myReduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) -> Result) -> Result {
        var sum = initialResult
        for element in self {
            sum = nextPartialResult(sum, element)
        }
        return sum
    }
}
  • 使用我们自定义的reduce
let array = [1,2,3,4,5,6,7,8,9,0]
let sum = array.myFilter{$0 % 2 == 1}.myReduce(0) { (total, num) in total + num
}
print(sum)
//打印结果25
  • 关于map, filter, reduce 函数大家可以看下张图,并自己练习。可以试试它们的实现。
23147344.png

Swift函数编程中的不变性

  • 不变性是函数式编程的基础,更容易写出无副作用的函数。

变量和不变量

var mutable //变量
let immutable = 1 //不变量

不可变类

struct Person {
    let name: String
}
  • 结构体是不可继承的
  • let 声明实例变量后,变量无法被改变
  • struct是值类型,传递过程是值传递

函数式编程Monad

  • 软件最基本的数据就是各种值
  • 处理值的一系列操作,可以封装成函数。输入一个值,会得到另一个值。下图的"(+3)"就是一个函数,对输入的值加上3,再输出。
bg2015071604.png
  • 函数很像漏斗,上面进入一个值,下面出来一个值。
bg2015071605.png
  • 函数可以连接起来使用,一个函数接着另一个函数。
bg2015071606.png
  • 函数还可以依次处理数据集合的每个成员。
bg2015071607.png
  • 说完了函数,再来看第二个概念:数据类型(type)。
    数据类型就是对值的一种封装,不仅包括值本身,还包括相关的属性和方法。下图就是2的封装,从此2就不是一个单纯的值,而是一种数据类型的实例,只能在数据类型的场景(context)中使用。
bg2015071608.png
  • 2变成数据类型以后,原来的函数就不能用了。因为"(+3)"这个函数是处理值的(简称"值函数"),而不是处理数据类型的。
bg2015071609.png
  • 我们需要重新定义一种运算。它接受"值函数"和数据类型的实例作为输入参数,使用"值函数"处理后,再输出数据类型的另一个实例。上图的fmap就代表了这种运算。
bg2015071610.png
  • 我们需要重新定义一种运算。它接受"值函数"和数据类型的实例作为输入参数,使用"值函数"处理后,再输出数据类型的另一个实例。上图的fmap就代表了这种运算。
bg2015071611.png
  • 一个有趣的问题来了。如果我们把函数也封装成数据类型,会怎样?
    下图就是把函数"(+3)"封装成一种数据类型。
bg2015071612.png
  • 这时,就需要再定义一种新的运算。它不是值与值的运算,也不是值与数据类型的运算,而是数据类型与数据类型的运算。
    下图中,两个数据类型进行运算。首先,取出它们各自的值,一个是函数,一个是数值;然后,使用函数处理数值;最后,将函数的返回结果再封装进数据类型。
bg2015071613.png
  • 函数可以返回值,当然也可以返回数据类型。
bg2015071614.png
  • 函数可以返回值,当然也可以返回数据类型。
bg2015071615.png
  • 我们需要的是这样一种函数:它的输入和输出都是数据类型。
bg2015071616.png
  • 这样做的好处是什么?
    因为数据类型是带有运算方法的,如果每一步返回的都是数据类型的实例,我们就可以把它们连接起来。
bg2015071617.png
  • 来看一个实例,系统的I/O提供了用户的输入。
bg2015071618.png
  • getLine函数可以将用户的输入处理成一个字符串类型(STR)的实例。
bg2015071619.png
  • readfile函数接受STR实例当作文件名,返回一个文件类型的实例。
bg2015071620.png
  • putStrLn函数将文件内容输出。
bg2015071621.png
  • 当然这只是一些概念,作为初学者,我也不是太明白,接下来我会通过一些列子,去贯穿这些概念。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容