什么是函数式编程
我们知道面向过程编程,面向对象编程,面向协议编程...大家听说过面向函数编程吗?
函数式编程是种编程范式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是 λ 演算(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 函数大家可以看下张图,并自己练习。可以试试它们的实现。
Swift函数编程中的不变性
- 不变性是函数式编程的基础,更容易写出无副作用的函数。
变量和不变量
var mutable //变量
let immutable = 1 //不变量
不可变类
struct Person {
let name: String
}
- 结构体是不可继承的
- let 声明实例变量后,变量无法被改变
- struct是值类型,传递过程是值传递
函数式编程Monad
- 软件最基本的数据就是各种值
- 处理值的一系列操作,可以封装成函数。输入一个值,会得到另一个值。下图的"(+3)"就是一个函数,对输入的值加上3,再输出。
- 函数很像漏斗,上面进入一个值,下面出来一个值。
- 函数可以连接起来使用,一个函数接着另一个函数。
- 函数还可以依次处理数据集合的每个成员。
- 说完了函数,再来看第二个概念:数据类型(type)。
数据类型就是对值的一种封装,不仅包括值本身,还包括相关的属性和方法。下图就是2的封装,从此2就不是一个单纯的值,而是一种数据类型的实例,只能在数据类型的场景(context)中使用。
- 2变成数据类型以后,原来的函数就不能用了。因为"(+3)"这个函数是处理值的(简称"值函数"),而不是处理数据类型的。
- 我们需要重新定义一种运算。它接受"值函数"和数据类型的实例作为输入参数,使用"值函数"处理后,再输出数据类型的另一个实例。上图的fmap就代表了这种运算。
- 我们需要重新定义一种运算。它接受"值函数"和数据类型的实例作为输入参数,使用"值函数"处理后,再输出数据类型的另一个实例。上图的fmap就代表了这种运算。
- 一个有趣的问题来了。如果我们把函数也封装成数据类型,会怎样?
下图就是把函数"(+3)"封装成一种数据类型。
- 这时,就需要再定义一种新的运算。它不是值与值的运算,也不是值与数据类型的运算,而是数据类型与数据类型的运算。
下图中,两个数据类型进行运算。首先,取出它们各自的值,一个是函数,一个是数值;然后,使用函数处理数值;最后,将函数的返回结果再封装进数据类型。
- 函数可以返回值,当然也可以返回数据类型。
- 函数可以返回值,当然也可以返回数据类型。
- 我们需要的是这样一种函数:它的输入和输出都是数据类型。
- 这样做的好处是什么?
因为数据类型是带有运算方法的,如果每一步返回的都是数据类型的实例,我们就可以把它们连接起来。
- 来看一个实例,系统的I/O提供了用户的输入。
- getLine函数可以将用户的输入处理成一个字符串类型(STR)的实例。
- readfile函数接受STR实例当作文件名,返回一个文件类型的实例。
- putStrLn函数将文件内容输出。
- 当然这只是一些概念,作为初学者,我也不是太明白,接下来我会通过一些列子,去贯穿这些概念。