swift 之函数式编程(一)

1. 什么是函数式编程?

函数式编程是阿隆佐思想的在现实世界中的实现, 它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及异变物件。 函数式编程的最重要基础是λ演算。而且λ演算的函數可以接受函數當作輸入(引數)和輸出(傳出值),函數式編程更加強調程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。函数式编程的杀手锏正是当今世界上日益增长的并行性编程和元数据编程趋势。其主要思想就是把运算过程尽量写成一系列嵌套的函数调用

2. 函数式编程的特点:

  • 函数是“第一等公民”:函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值
  • 只用“表达式”,不用语句:“表达式”--是一个单纯的运算过程,总是有返回值; ‘语句’--执行某种操作,没有返回值。函数式编程要求,只用表达式,不使用语句,也就是说,每一步都是单纯的运算,而且没有返回值。但在实际的I/O是不可能的,因此,编程过程中,函数式编程只要求把I/O限制到最小,不要有不必要的读写行为,保持计算过程的单纯性。
  • 没有副作用:函数要保持独立,所有的功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值
  • 不修改状态: 函数式编程只是返回新的值,不修改系统变量。
  • 引用透明: 函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的

3.入手一门新的语言的 时候,一般关注的内容有:
1.原生的数据结构
2.运算符
3.分支控制
4.如果是面向对象的编程语言,其面向对象的实现是怎样的
5.如果是函数式编程语言,其面向函数式编程的实现是怎么样的
6.如果是面向接口的编程语言,器面向接口是如何实现的
7.如果是支持泛型编程,那么又是如何实现的
 
 
4.对于支持函数式编程的语言,其一般的特点可能包含以下几种:
1. 支持递归
2. 函数本身是语言First Class 的组成要素,且支持高阶函数和闭包
3. 函数调用尽可能没有副作用(Side Effect)的条件 
    前面的1、2 swift是毋庸置疑的。对于条件3:
    为了减少函数的副作用,很多函数式编程语言都力求达到所谓的“纯函数”。纯函数是指函数与外界交换数据的唯一渠道是参数和返回值,而不会受到外部变         量的干扰。这似乎是跟闭包的概念相抵触。因为闭包本身的一个重要的特点就是可以访问到函数定义时的上下文环境。因为闭包本身的一个重要的特点就是可以       访问到函数定义时的上下文环境。
           事实上,为了支持这种情况下的纯函数,一些编程语言提供的数据结构式不可变的(Persist)。比如说,在 Python 中,字符串str就是一类不可变的数据       结构。 你不能在原来的字符串上进行修改,每次想要进行类似的操作,其实都是生成了一个新的str对象。 然而 Python 中的链表结构则是可变的。
a = "hello ,"
b = a 
a += 'world'
print a   # hello ,world
print b   # hello ,

    swift中更多的是值类型,而不是引用类型,你会发现Int,Float,String,Array,Dictionary等都是Struct类型,而Struct都是值类型【暗示了结构体应该主要         用于封装数据】,当然Enum也是值类型

     在swift中区分值类型和引用类型是为了将可变和不可不区分开来。值类型的数据传递给函数,函数内部可以自由拷贝,改变值,而不用担心产生副作用。在多线      程环境下,多个线程同时运行,可能会意外错误地修改数据,这常常会是一种难以调试的bug。而使用值类型,你可以安全地在线程间传递数据,因为值类型传递      是拷贝,所以无需在线程间同步数据变化。这就可以保证代码线程环境下的安全性。

     swift中的参数定义中加入inout,这样的话就可以通过参数来修改变量。这个特性很有C的风格。

     使用swift自带的数据结构并不能很好的的实现“无副作用”的“纯函数式”编程。幸好作为一种关注度很高的语言, 已经有开发者为其实现了一套完全满足不可变要      求的数据结构和库:Swiftz。坚持使用let和swiftz提供的数据结构来操作,就可以实现“纯函数式”编程。

     不变性有诸多好处

  • 更高层次的抽象。程序员可以以更接近数学的方式思考问题。

  • 更容易理解的代码。由于不存在副作用,无论多少次执行,相同的输入就意味着相同的输出。纯函数比有可变状态的函数和对象理解起来要容易简单得多。你无需再担心对象的某个状态的改变,会对它的某个行为(函数)产生影响。

  • 线程安全的代码。这意味着多线程环境下,运行代码没有同步问题。它们也不可能因为异常的发生而处于无法预测的状态中。

惰性求值

惰性计算是函数式编程语言的一个特性。惰性计算的表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值。惰性计算有如下优点。

  • 首先,你可以用它们来创建无限序列这样一种数据类型。因为直到需要时才会计算值,这样就可以使用惰性集合模拟无限序列。
  • 第二,减少了存储空间。因为在真正需要时才会发生计算。所以,节约了不必要的存储空间。
  • 第三,减少计算量,产生更高效的代码。因为在真正需要时才会发生计算。所以,节约那部分没有使用到的值的计算时间。例如,寻找数组中第一个符合某个条件的值。找到了之后,数组里该值之后的值都可以不必计算了。
func doSomeWork(optional:Int?,defaultValue:Int)->Int{
    if let opt = optional{
        return opt
    }else{
        return defaultValue
    }
}

func doSomeWorkNew(optional:Int?, defaultValue:()->Int)->Int{
    
    if let opt = optional{
        return opt
    }else{
        return defaultValue()
    }
}

let value:Int? = 1
doSomeWork(value, (2+5))
doSomeWorkNew(value, { () -> Int in
    return 2+5
})

上面的代码可以感觉到惰性求值的好处。

swift也提供了支持惰性求值的语法:下面展示了将默认是严格求值的数组变为惰性序列:

let r = 1...3
let seq = lazy(r).map {
    (i: Int) -> Int in
    println("mapping \(i)")
    return i * 2
}

for i in seq {
    println(i)
}

 

      Swift对函数式编程的支持,使得程序员多了一种选择。Swift并不强迫程序员一定要以面向对象的方法思维。在场景合适的情况下,程序员可以选择使用函数式风格编写代码。如果确实是合适的场景,就能够改善生产力。现实的选择是支持面向对象编程的同时,提供函数式的支持。这样,在大部分面向对象游刃有余的地方,仍然可以使用面向对象的方法。而在适合函数式编程的地方,而你又拥有函数式编程的思维和能力时,还可以采用函数式的编程方法改善生产力。

 

接下来的系列文章中我将以《Functional Programing in Swift》这本书,来一起学习swift的函数式编程。

 

 

 
posted @ 2015-07-29 08:10  OHeroJ  阅读(2018)  评论(0编辑  收藏  举报