文章内容来自《Functional Programing in Swift》,具体内容请到书中查阅

Map, Filter, Reduce

Functions that take functions as arguments are sometimes called higher- order functions.

higher-order function(高阶函数)就是说函数可以作为另一个函数的参数。

在本章,我们将介绍一下swift标准库中在数组方面的一些相关的高阶函数,先介绍一些比较普通的然后介绍如何把这些泛型的操作组织成一个比较复杂的操作。

Introducing Generics (泛型)

// 函数:返回 对原来的数组的每个元素进行加1后的数组
func incrementArray(xs: [Int]) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(x + 1)
} return result
}
// 函数:返回 对原来的数组的每个元素进行乘2后的数组
func doubleArray1(xs: [Int]) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(x * 2)
}
return result
}

你会发现这两个方法中有很多相似的代码,能够将这些不同的地方抽象出来,写成一个单一,更普通的函数的方式么?就如下面:

func computeIntArray(xs: [Int]) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(/* something using x */) }
return result
}

为了完成上面的定义,我们将需要一个新的参数用来抽象出这些不同的地方:

func computeIntArray(xs: [Int], f: Int -> Int) -> [Int] {
var result: [Int] = []
for x in xs {
result.append(f(x))
}
return result
}

那么之前的代码将变成下面:

func doubleArray2(xs: [Int]) -> [Int] {
return computeIntArray(xs) { x in x * 2 }
}

注意在调用computeIntArray的时候,我们使用过了尾闭包(trailing closures )语法

但是这个computeIntArray还不是很灵活,如果我们支持计算一个数组中的元素是不是偶数:

func isEvenArray(xs: [Int]) -> [Bool] {
computeIntArray(xs) { x in x % 2 == 0 }
}

不幸的是,这段代码会报一个类型错误。那么我们该如何解决呢?或许我们会这样做:

func computeBoolArray(xs: [Int], f: Int -> Bool) -> [Bool] {
let result: [Bool] = []
for x in xs {
result.append(f(x))
}
return result
}

但是这样还是不好,假如我们需要计算的是String?难道我们要定义另外一个高阶函数,参数类型为 Int->String  ?

we will say "NO"

幸好,这有解决办法:我们可以使用泛型 (generics):

func genericComputeArray<U>(xs: [Int], f: Int -> U) -> [U] {
var result: [U] = []
for x in xs {
result.append(f(x))
}
return result
}

但是你会发现我们这边还是有个[Int],so:

func map<T, U>(xs: [T], f: T -> U) -> [U] {
var result: [U] = []
for x in xs {
result.append(f(x))
}
return result
}

下面我们对上面的一个函数进行改造:

func computeIntArray<T>(xs: [Int], f: Int -> T) -> [T] {
return map(xs, f)
}

map这个函数的早已在swift的标准库中定义了。下面就是用swift中的map的调用:

func doubleArray3(xs: [Int]) -> [Int] {
return xs.map { x in 2 * x }
}

在本章中我们不建议你自定义map,但是我们想说的是定义map其实也不是那么神奇的一件事,你完全可以自己自定义。

Filter

map函数并不是swift标准Array库中通过使用 泛型 唯一的方法。在接下来的节中,我们将会陆续介绍其他的

假定我们有下面一个数组:

let exampleFiles = ["README.md", "HelloWorld.swift", "HelloSwift.swift", "FlappyBird.swift"]

现在假定我们需要元素的后缀是.swift的数组:

func getSwiftFiles(files: [String]) -> [String] {
var result: [String] = []
for file in files {
if file.hasSuffix(".swift") {
result.append(file)
}
}
return result
}

为了满足更多的需求,我们使用泛型定义了:

func filter<T>(xs: [T], check: T -> Bool) -> [T] {
var result: [T] = []
for x in xs {
if check(x) {
result.append(x)
}
}
return result
}

那么这个时候定义getSwiftFiles就Filter而言变得更加容易:

func getSwiftFiles2(files: [String]) -> [String] {
return filter(files) { file in file.hasSuffix(".swift") }
}

就如map一样,filter也早已在swift的标准库中定义了:

exampleFiles.filter { file in file.hasSuffix(".swift") }

现在你可能想知道:有没有一个更通用的函数----定义包含了 map 和fiter的函数?这就是接下来我们要讲的。

Reduce

来一起看下面这个sum函数

func sum(xs: [Int]) -> Int {
var result: Int =
for x in xs {
result += x
}
return result
}

下面我们使用下这个函数

let xs = [1,2,3,4]

sum(xs)   // 结果为10

一个和sum比较类似的函数product:

func product(xs: [Int]) -> Int {
var result: Int =
for x in xs {
result = x * result
}
return result
}

或者类似的一个,把数组中的字符串串起来,返回一个字符串:

func concatenate(xs: [String]) -> String {
var result: String = ""
for x in xs {
result += x
}
return result
}

或者我们:

func prettyPrintArray(xs: [String]) -> String {
var result: String = "Entries in the array xs:\n"
for x in xs {
result=" "+result+x+"\n"
}
return result
}

上面的这些函数有公共的地方么?他们都初始化了一个变量result,他们都通过迭代xs里面的元素 以某种方式进行更新这个reslut。用泛型函数来描绘这个模式,我们需要提炼出2条信息:

初始化一个变量,在迭代中更新这个变量。

鉴于此,我们通过定义reduct函数来描述这个模式:

func reduce<A, R>(arr: [A], initialValue: R,combine: (R, A) -> R) -> R {
var result = initialValue
for i in arr {
result = combine(result, i)
}
return result
}

一开始对于这个reduce会有一点难读。它在两方面使用泛型:输入数组的类型[A],resulst 的类型R。在函数式编程中如OCaml和Haskell中,reduce函数被叫做fold或者fold_right

func sumUsingReduce(xs: [Int]) -> Int {
return reduce(xs, ) { result, x in result + x } // 使用了尾闭包
}

我们甚至可以简写成:

func productUsingReduce(xs: [Int]) -> Int {
return reduce(xs, , *)
} func concatUsingReduce(xs: [String]) -> String {
return reduce(xs, "", +)
}

在一次,reduce是array的一个扩展,所以我们在使用reduce(xs, initialValue,combine)变为:xs.reduce(initialValue, combine)

reduce: iterating over an array to compute a result.

Putting It All Together 

struct City{
let name: String
let population: Int
} let paris = City(name: "Paris", population: )
let madrid = City(name: "Madrid", population: )
let amsterdam = City(name: "Amsterdam", population: )
let berlin = City(name: "Berlin", population: ) let cities = [paris, madrid, amsterdam, berlin] func scale(city: City) -> City {
return City(name: city.name, population: city.population * )
} cities.filter({ city in city.population > }) .map(scale).reduce("City: Population") {
result, c in
return result + "\n" + "\(c.name) : \(c.population)"
}

Generics vs. the Any Type 

两者都可以在函数定义中接受不同类型的参数,然而又有所不同:

generics can be used to define flexible functions, the types of which are still checked by the compiler; the Any type can be used to dodge Swift’s type system (and should be avoided whenever possible).

func noOp<T>(x: T) -> T {
return x
}

用Any:

func noOpAny(x: Any) -> Any {
return x
}

两者关键的不同是我们知道返回值的类型,对于泛型noOp,我们可以清楚的知道返回值的类型跟输入进来的值的类型相同。但都对于Any

并不适合,它的返回值可以是任何类型。使用Any会逃避swift的类型系统。这样的话Any有各种各样的可能的运行时错误。

在这里泛型和Any的区别我粗略的讲解了一下,下次我会另开一篇文章介绍两者的区别

swift之函数式编程(四)的更多相关文章

  1. swift 之函数式编程(一)

    1. 什么是函数式编程? 函数式编程是阿隆佐思想的在现实世界中的实现, 它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及异变物件. 函数式编程的最重要基础是λ演算.而且λ演算的函數可以接受函 ...

  2. swift之函数式编程

    函数式编程初探 最近初学swift,和OC比,发现语言更现代,也有了更多的特性.如何写好swift代码,也许,熟练使用新特性写出更优秀的代码,就是答案.今天先从大的方向谈谈swift中的编程范式-函数 ...

  3. swift之函数式编程(二)

    本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...

  4. 最通俗易懂的方式让你理解 Swift 的函数式编程

    函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心.善用函数式编程思路,可以对我们的开发工作有很大的帮助和 ...

  5. swift之函数式编程(三)

    文章来源于<Functional Programing in Swift>,本系列仅仅是观后概括的一些内容 Wrapping Core Image 上一篇文章我们介绍了 高阶函数并且展示了 ...

  6. swift之函数式编程(五)

    文章内容来源于<Functional Programing in Swift>,详情请看原著 The Value of Immutability swift 对于控制值改变有一些机制.在这 ...

  7. 函数式编程-将Monad(单子)融入Swift

    前言 近期又开始折腾起Haskell,掉进这个深坑恐怕很难再爬上来了.在不断深入了解Haskell的各种概念以及使用它们去解决实际问题的时候,我会试想着将这些概念移植到Swift中.函数式编程范式的很 ...

  8. Swift の 函数式编程

    Swift 相比原先的 Objective-C 最重要的优点之一,就是对函数式编程提供了更好的支持. Swift 提供了更多的语法糖和一些新特性来增强函数式编程的能力,本文就在这方面进行一些讨论. S ...

  9. swift语言的特征:类型系统与函数式编程:swift是面向类型和面向函数编程的语言

    swift语言的特征: 类型系统:值类型与引用类型.泛型.协议类型 函数式编程:

随机推荐

  1. Hadoop的safeMode

    当集群启动的时候,会首先进入到安全模式.系统在安全模式下,会检查数据块的完整性.假设我们设置的副本数(即参数dfs.replication)是5,那么在dataNode上就应该有5个副本存在,假设只存 ...

  2. Openlayers系列(一)关于地图投影的理解

    背景 近期开发以MongoDB为基础的分布式地理数据管理平台系统,被要求做一个简单的demo给客户进行演示.于是笔者便打算向数据库中存储一部分瓦片数据,写一个简单的存取服务器,使用Openlayers ...

  3. TCP/IP中你不得不知的十大秘密

    这段时间 有一点心很浮躁,不过希望自己马上要矫正过来.好好学习编程!这段时间我想好好地研究一下TCP/IP协议和网络传输这块!加油 一.TCP/IP模型 TCP/IP协议模型(Transmission ...

  4. AngularJS - 依赖注入(Dependency Injection)

    点击查看AngularJS系列目录 转载请注明出处:http://www.cnblogs.com/leosx/ 依赖注入 依赖注入是软件设计模式中的一部分,用于处理组件是如何得到它说依赖的其它组件的. ...

  5. 快速搭建应用服务日志收集系统(Filebeat + ElasticSearch + kibana)

    快速搭建应用服务日志收集系统(Filebeat + ElasticSearch + kibana) 概要说明 需求场景,系统环境是CentOS,多个应用部署在多台服务器上,平时查看应用日志及排查问题十 ...

  6. BZOJ-1010-[HNOI2008]玩具装箱toy(斜率优化)

    Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1... ...

  7. R语言基础语法

    学习一门新的语言,率先学习输出hello world.我们就从这里开始学习. 首先打开RStudio这个IDE,然后在左边输入: > mystr <- "hello world& ...

  8. FastDFS的安装步骤

    1.安装相关环境 yum install -y gcc-c++ yum -y install libevent yum install -y pcre pcre-devel yum install - ...

  9. Oracle添加含有脏数据的约束

    需求: 一个表的唯一约束被禁用期间,有脏数据进来,当启用约束时失败. 环境: -bash-4.1$ uname -a Linux dbtest1 2.6.32-279.el6.x86_64 #1 SM ...

  10. apache一个ip多个端口虚拟主机

    1.打开httpd.conf,查找Listen:80,在下面一行加入Listen:8080:2.查找#Include conf/extra/httpd-vhosts.conf,将此行前面的#去掉:3. ...