登录 |  注册
首页 >  移动开发 >  Swift >  Swift函数式编程函数

Swift函数式编程函数

Swift函数式编程-函数

Swift支持函数式编程,这一篇介绍Swift中的函数。

高阶函数(Higher order function)

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

Swift中的函数都是高阶函数,这和Scala,Haskell一致。与此对照的是,Java中没有高阶函数(Java 7支持闭包之前)。Java中方法没法单独存在,方法总是需要和类捆绑在一起。当你需要将一个函数传递作为参数给另外一个函数时,需要一个类作为载体来携带函数。这也是Java中监听器(Listener)的做法。

高阶函数对于函数式语言很重要,原因至少有两个:

  • 首先,高阶函数意味着您可以使用更高的抽象,因为它允许我们引入计算的通用方法。例如,可通过抽象出一个通用机制,遍历数组并向其中的每个元素应用一个(或多个)高阶函数。高阶函数可以被组合成为更多更复杂的高阶函数,来创造更深层的抽象。

  • 其次,通过支持函数作为返回值,就可支持构建动态性与适应性更高的系统。

一等函数(First class function)

一等函数,进一步扩展了函数的使用范围,使得函数成为语言中的“头等公民”。这意味函数可在任何其他语言结构(比如变量)出现的地方出现。一等函数是更严格的高阶函数。Swift中的函数都是一等函数。

闭包

闭包是一个会对它内部引用的所有变量进行隐式绑定的函数。也可以说,闭包是由函数和与其相关的引用环境组合而成的实体。

函数实际上是一种特殊的闭包,你可以使用{}来创建一个匿名闭包。使用 in 来分割参数和返回类型。

let r = 1...3
let t = r.map { (i: Int) -> Int in
    return i * 2
}

map函数遍历了数组,用闭包处理了所有元素。并返回了一个处理过的新数组。

Objective-C在后期加入了对闭包支持。闭包是一种一等函数。通过支持闭包,Objective-C拓展其语言表达能力。但是如果将Swift的闭包语法与Objective-C的闭包相比,Swift的闭包显得相当简洁和优雅,Objective-C的闭包则显得有些繁重复杂。

函数柯里化(Function Curring)

函数柯里化(Function Curring),是指接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,该函数返回一个接受余下参数的新函数。这个名词来源于逻辑学家 Haskell Curring。编程语言Haskell也取自这位逻辑学家的名字。

Haskell中函数都可以柯里化。在Haskell里的函数参数的型别声明也暗示了函数是柯里化的。Haskell中,返回值和参数之间,各个参数之间都是以->分隔。这是因为,如果你向可以接受多个参数的函数传入一个参数,函数仍然有返回值。它的返回值是另外一个函数。这个函数可以接受剩余的参数,我们称这个返回的函数为不全呼叫函数。本质上讲,Haskell的所有函数都只有一个参数。

下面语句在命令行中展示了Haskell里max的型别:

Prelude> :type max
max :: Ord a => a -> a -> a

其实也可以写作:

max :: (Ord a) => a -> (a -> a)

这意味着,如果向max传入一个参数a,将返回一个型别为(a -> a)的函数。

柯里化为构造新函数带来了方便。也免除了一些一次性的中间函数的编写工作。

Swift可以写出柯里化函数,虽然它还是保留了和Java相似的非柯里化函数的写法。以max函数为例,Swift中柯里化函数如下:

func max(a: Int)(b: Int) -> Int {
    return a > b ? a : b;
}

let max3 = max(3)
max3(b: 5)

函数式思维

使用函数解决问题

一个简单的例子,找出1到10这个数组里的奇数。使用Java语言的思维(循环控制其实是过程式语言的思维),一般的写法会是这样:

var odds = [Int]()
for i in 1...10 {
    if i % 2 == 1 {
        odds.append(i)
    }
}

println(odds)

输出结果为:[1, 3, 5, 7, 9]。而函数式的写法更为简单:

odds = Array(1...10).filter { $0 % 2 == 1 }
println(odds)

函数式的写法更为简单的原因是,放弃了对循环的控制,而使用函数处理序列。如何处理序列,即循环体里应该写的代码,在函数式编程中是由一个函数(一般会是闭包)传入。在计算机的底层对语言的实现中,仍然使用了循环控制这样的概念。但是,在编写函数式编程语言时,你并不需要这个概念。

另外一个简单的例子,如何找出1到10这个数组里的奇数,并且求它们的和呢?一般的写法会是这样:

var sumOdds = 0
var odds = [Int]()
for i in 1...10 {
    if i % 2 == 1 {
        odds.append(i)
        sumOdds += i
    }
}
println(sumOdds)

而函数式版本会是这样:

let sum = Array(1...10)
        .myFilter { (i) in i % 2 == 1}
        .reduce(0) { (total, number) in total + number }
println(sum)

如果序列中的某些值做操作,过程式语言中,由于存在循环变量,就可以对循环所处的位置进行判断。而函数式编程语言的做法是使用函数构建一个符合条件的新序列,这里是Array(1...10).myFilter { (i) in i % 2 == 1},用于代表1到10里的奇数。然后再对新序列做进一步操作。这个例子中,使用reduce函数对新序列求和。

Haskell这种纯函数式编程语言,由于不需要,是没有循环控制语句的,你看不到for,while这样的关键字。但在Swift中,程序员在使用更高层级的抽象的同时意味着需要放弃对细节的控制。但是,这并不意味着无法在需要的时候回收控制。以函数式思维的一个重要方面是知道放弃多少控制,以及何时放弃。

使用函数组合

函数式编程思想中,面对复杂问题时,会使用一个个函数组合来为复杂问题建模。我们使用一个判断质数的例子来表现函数式编程的这一特点。我们会分别使用面向对象编程和函数式编程实现判断质数的算法,以对比两者的不一样。

质数是因数只能是及其本身的整数。我们将使用这种算法:首先找出数字的因数,然后求所有因数的和,如果所有因数和为该数字加一,就可以确定该数字是质数。

为了先用面向对象的一般写法来实现该算法:

class PrimeNumberClassifier {

   let number: Int

   init(number: Int){
        self.number = number
   }

    func isFactor(potential: Int) -> Bool {
        return number % potential == 0
    }

    func getFactors() -> [Int] {
        var factors : [Int] = Array<Int>()
        for it in 1...number {
            if isFactor(it) {
                factors.append(it)
            }
        }
        return factors
    }

    func sumFactors() -> Int {
        let factors = getFactors()
        var sum = 0
        for factor in factors {
            sum += factor
        }
        return sum
    }

    func isPrime() -> Bool {
        return self.sumFactors() == number + 1
    }
}

接着我们使用函数式写法:

func isFactor(number: Int)(potential: Int) -> Bool {
  return (number % potential) == 0
}

func factors(number: Int) -> [Int] {
    let isFactorForNumber = isFactor(number)
    return Array(1...number).filter {
        isFactorForNumber(potential: $0)}
}

func sumFactors(number: Int) -> Int {
    return factors(number).reduce(0){ (total, num) in 
        total + num }
}

func isPrime(number: Int) -> Bool {
    return sumFactors(number) == number + 1
}

可以看到,我们定义了四个函数,每个函数解决一个更小的问题。最后在isPrime为起点,把所有函数都串了起来,组成了整个算法实现。由于Swift中的函数都是一等函数。所以,我们可以使用filter和reduce这样接受闭包的函数提供对筛选和求和更简洁的表达方式。函数式写法中,所有的函数都是无状态的,无副作用的。也就是说无论你调用几次,只要函数的输入参数确定了,函数的输出就确定了。由于无状态,这里的每个函数都是易于复用的。你可以在任何外部模块放心地使用这些函数,而不用像在面向对象语言中那样担心对象的某个状态会对你调用的函数产生影响。

总结

函数式编程的核心是函数,函数是“头等公民”。这就像面向对象语言的主要抽象方法是类。Swift中的函数具有函数式语言中的函数的所有特点。这种支持使得你可以很容易地使用Swift写出函数式风格的代码。

原文出处:http://lincode.github.io/Swift-FirstOrder-Func
作者:LinGuo

上一篇: Swift编程风格指南
下一篇: Swift函数式编程不变性
推荐文章
  • 在iOS开发中,要实现发送电子邮件的功能,可以使用MFMailComposeViewController类,这是Apple提供的一个内置框架——MessageUI的一部分。以下是在Swift中使用MFMailComposeViewController来发送邮件的基本步骤:实例步骤1、创建一个简单的V
  • iOS故事板(Storyboards)是Apple在iOS5中引入的一种可视化界面构建工具,它允许开发者在一个单一的文件中设计和管理整个应用程序的所有用户界面屏幕以及它们之间的导航关系。故事板通过InterfaceBuilder集成到Xcode开发环境中,为开发者提供了一种直观的方式来组织UI元素、
  • iOS自动布局(AutoLayout)是Apple在iOS6及以后版本引入的一种动态布局系统,用于处理用户界面元素在不同屏幕尺寸和方向下的自适应布局。通过自动布局,开发者可以创建灵活的、可伸缩的UI设计,确保应用在各种iPhone、iPad设备以及不同的屏幕旋转状态下都能保持良好的视觉效果和用户体验
  • ios(InternetworkingOperatingSystem-Cisco,缩写ios,也可写作IOS,区别于苹果系统iOS)iOS是由苹果公司(AppleInc.)开发的一种专有移动操作系统,它为苹果的多款移动设备提供用户界面和底层操作逻辑。最初随iPhone在2007年发布时被称为“iPh
  • 小程序中的组件也是由宿主环境提供的,开发者可以使用组件快速搭建出页面结构,官方把小程序里的组件分为了9大类,分别是视图容器基础内容表单组件导航组件媒体组件map地图组件canvas画布组件开放能力无障碍访问常见的视图容器类组件1.view普通视图区域类似于HTML中的div,是一个块级元素常用于实现
  • 一、下载并安装开发工具uniapp的开发工具为HbuilderX,下载地址为:去下载这里选择windows版本。windows版本下载完成之后会得到一个zip的压缩包文件,解压完成即可使用,是不需要安装的绿色版本。二、安装sass和scss插件打开HbuilderX,点击工具----插件安装,如图所
学习大纲