[连载]Swift开发入门(06)--- 函数式编程

??面向对象编程和函数式编程是目前最主流的两种编程范式,而关于这两种范式孰优孰劣的讨论一直都没有停止过。事实上,真正理解两种编程范式的程序员不会武断的说这二者孰优孰劣,因为任何编程语言都没有什么灵丹妙药让其使用者成为优秀的程序员。其实,像Java这样很经典的面向对象的编程语言,也能够看到函数式编程的影子,如果你使用过访问者模式、命令模式,如果你使用过接口回调,你实际上已经使用了函数式编程的理念,而且在新版本的Java中,已经开始支持Lambda表达式和函数式接口,这些都是Java为了支持函数式编程所作出的改进。同样,我们也可以用C语言写出面向对象风格的代码。其实,只要在适当的地方使用适当的编程范式就能够写出优质的代码,我们不应该让自己的程序囿于某一种编程范式,就如同一个优秀的程序员绝不会声称自己效忠于某种语言(有很多蹩脚的三流程序员就会做这样的事情)。

简单数组过滤

??让我们看这样一个例子,在一个数组中放入一组偶数,传统的做法是这样的。

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

println(evens)      // [2, 4, 6, 8, 10]

??如果用函数编程的方式,我们可以用下面的代码来做同样的事情。而且我们将判断偶数的代码写成一个函数,明显增加了代码的可重用性。

func isEven(number: Int) -> Bool {
    return number % 2 == 0
}

var evens = Array(1...10).filter(isEven)
println(evens)

??当然,也可以写成下面的样子。

var evens = Array(1...10).filter{ $0 % 2 == 0 }
println(evens)      // [2, 4, 6, 8, 10]

??明显,函数式编程有如下一些特性:

?? - 高阶函数:函数可以作为函数的参数传给函数。

?? - 一等公民:可以将函数视为变量来使用。

?? - 闭包:可以使用匿名函数。

归约(Reducing)

??如果要在上面的例子再增加一项功能,将数组中的偶数求和,传统的做法如下所示。

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

var evenSum = 0
for i in evens {
    evenSum += i
}

println(evenSum)    // 30

??用函数式编程的方式重新编写上面的代码,如下所示。

var evenSum = Array(1...10)
    .filter { $0 % 2 == 0}
    .reduce(0) { $0 + $1 }

println(evenSum)    // 30

??如果想理解reduce是如何工作的,可以看看函数的原型。

func reduce<U>(initial: U, combine: @noescape (U, T) -> U) -> U

索引器

??我们再来完成一个新的任务,为数组中的元素建立索引。假如有一个数组中有一系列的字符串,我们希望通过字符串的首字母作为索引来建立一个新的存储结构,传统的做法如下所示。

import Foundation

let words = [
    "Cat", "Chicken", "Fish", "Dog", "Mouse",  "Pig", "Monkey"
]

typealias Entry = (Character, [String])

func buildIndex(words: [String]) -> [Entry] {
    var result = [Entry]()

    var letters = [Character]()
    for word in words {
        let firstLetter = Character(word.substringToIndex(
            advance(word.startIndex, 1)).uppercaseString)

        if !contains(letters, firstLetter) {
            letters.append(firstLetter)
        }
    }

    for letter in letters {
        var wordsForLetter = [String]()
        for word in words {
            let firstLetter = Character(word.substringToIndex(
                advance(word.startIndex, 1)).uppercaseString)

            if firstLetter == letter {
                wordsForLetter.append(word)
            }
        }
        result.append((letter, wordsForLetter))
    }

    return result
}

// [("C", ["Cat", "Chicken"]), ("F", ["Fish"]), ("D", ["Dog"]), ("M", ["Mouse", "Monkey"]), ("P", ["Pig"])]
println(buildIndex(words))

??使用函数式编程,可以将代码改写为如下所示的样子。

import Foundation

let words = [
    "Cat", "Chicken", "Fish", "Dog", "Mouse",  "Pig", "Monkey"
]

typealias Entry = (Character, [String])

func distinct<T: Equatable>(source: [T]) -> [T] {
    var unique = [T]()
    for item in source {
        if !contains(unique, item) {
            unique.append(item)
        }
    }
    return unique
}

func buildIndex(words: [String]) -> [Entry] {
    func firstLetter(str: String) -> Character {
        return Character(str.substringToIndex(advance(str.startIndex, 1)).uppercaseString)
    }

    return distinct(words.map(firstLetter)).map {
        (letter) -> Entry in return (letter, words.filter {
                firstLetter($0) == letter
        })
    }
}

println(buildIndex(words))

柯里化(currying)

??要理解柯里化,我们先看看下面的例子。

import Foundation

let data = "5,7;3,4;55,6"

// ["5,7", "3,4", "55,6"]
println(data.componentsSeparatedByString(";"))

// ["5", "7;3", "4;55", "6"]
println(data.componentsSeparatedByString(","))

??在上面的例子中,我们使用字符串的componentsSeparatedByString()方法根据指定的字符(串)将字符串拆分成字符串的数组。有些时候,我们可能需要用指定的字符(串)反复的对出现的字符串进行拆分,于是我们可以做出这样的处理,如下所示。

import Foundation

let data = "5,7;3,4;55,6"

func createSplitter(separator: String) -> (String -> [String]) {
    func split(source: String) -> [String] {
        return source.componentsSeparatedByString(separator)
    }

    return split
}

let commaSplitter = createSplitter(",")
// ["5", "7;3", "4;55", "6"]
println(commaSplitter(data))

// ["5,7", "3,4", "55,6"]
let semiColonSplitter = createSplitter(";")
println(semiColonSplitter(data))

??明显,按照上面的做法,我们可以重复的使用两种拆分器commaSplitter和semiColonSplitter对字符串进行拆分,而不用每次调用字符串的拆分函数并指定拆分字符(串)。这种编程理念通常称之为"部分化应用"(partial application),其原理是将函数中的一个或多个参数先固定下来,创建出一个新的函数。我们继续往下看。

import Foundation

let data = "5,7;3,4;55,6"

func createSplitter(separator: String)(source: String) -> [String] {
    return source.componentsSeparatedByString(separator)
}

let commaSplitter = createSplitter(",")
// ["5", "7;3", "4;55", "6"]
println(commaSplitter(source: data))

// ["5,7", "3,4", "55,6"]
let semiColonSplitter = createSplitter(";")
println(semiColonSplitter(source: data))

??这样看起来不是更加优雅吗?先传入一个参数,稍后再传入另一个参数来实现完整的功能,这其实就是所谓的函数的柯里化。让我们再来看一个例子吧。

func add(one: Int, two: Int, three: Int) -> Int {
    return one + two + three
}

let sum = add(1, 2, 3)
println(sum)

??我们也可以这样来写改写add()函数。

func add(one: Int)(two: Int)(three: Int) -> Int {
    return one + two + three
}

let step1 = add(1)
let step2 = step1(two: 2)
let step3 = step2(three: 3)
println(step3)

??再来看一个例子,实现一个柯里化的字符串填充函数。

func stringPadding(startIndex: Int, paddingString: String)(source: String, length: Int) -> String {
    return source.stringByPaddingToLength(length, withString: paddingString, startingAtIndex: startIndex)
}

let text = "Swift"
let dottedPadding = stringPadding(0, ".")
let paddingText = dottedPadding(source: text, length: 10)
println(paddingText)    // Swift.....
时间: 2024-10-10 07:59:10

[连载]Swift开发入门(06)--- 函数式编程的相关文章

[连载]Swift开发入门(05)--- 枚举

??枚举是定义符号常量的手段,它把一堆相似的值组织在一起.例如你在指定文字对齐方式的时候通常有三种可选的值:左对齐.右对齐和居中对齐:你在处理游戏中的方法时可能的取值有东.西.南.北.Swift中的枚举比你了解的其他语言的枚举更加强大,它的行为类似于类和结构体,它甚至可以有自己的方法,包括构造器. 创建枚举 ??我们先通过一个简单的例子来认识一下枚举.玩过扑克牌的都知道,扑克有四种花色,分别是黑桃(spade).红心(heart).草花(club)和方块(diamond),如果我们要做一个扑克游

20170917 前端开发周报:JavaScript函数式编程、作用域和闭包

1.用函数式编程对JavaScript进行断舍离 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! https://juejin.im/entry/59b86... 2.JavaScript作用域和闭包 作用域和闭包在JavaScript里非常重要.但是在我最初学习JavaScript的时候,却很难理解.这篇文章会用一些例子帮你理解它们.我们先从作用域开始.作用域 JavaScript的作用域限定了你可以访问哪些变量.有两种作

论 Swift 开发入门:搜索框(UISearchBar)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/44827923 ------------------------------------------------------------------------------------------ 代码示例 // // ViewController.swift // UISearchBarSample // // Created by jinnchang on 15/4/1. // C

论 Swift 开发入门:活动指示器(UIActivityIndicatorView)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/44828021 ------------------------------------------------------------------------------------------ 代码示例 // // ViewController.swift // UIActivityIndicatorViewSample // // Created by jinnchang on

论 Swift 开发入门 : 选择器(UIPickerView)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/44487269 ------------------------------------------------------------------------------------------ 1.Summary -------------------------------------------------------------------------------------

论 Swift 开发入门 : 滑块(UISlider)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/44487407 ------------------------------------------------------------------------------------------ 1.Summary -------------------------------------------------------------------------------------

论 Swift 开发入门 : 进度条(UIProgressView)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/44802019 ------------------------------------------------------------------------------------------ 概述 ------------------------------------------------------------------------------------------ 代

论 Swift 开发入门 : 按钮(UIButton)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/44403537 1.UIButton 概述 继承关系:UIButton -> UIControl -> UIView 控件样式: 2.UIButton 初始化 (1)使用 buttonWithType 构建按钮,已有的六种类型如下: enum UIButtonType : Int { case Custom // 自定义风格 case System // 圆角矩形 case Deta

论 Swift 开发入门 : 开关(UISwitch)

转载请声明出处:http://blog.csdn.net/jinnchang/article/details/44407193 1.UIButton 概述 继承关系:UISwitch -> UIControl -> UIView 2.控件样式 3.使用示例 var myButton: UIButton? var mySwitch: UISwitch? override func viewDidLoad() { self.myButton = UIButton.buttonWithType(.S