博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift 中的高阶函数和函数嵌套
阅读量:6624 次
发布时间:2019-06-25

本文共 3678 字,大约阅读时间需要 12 分钟。

高阶函数

在Swift中,函数可做为“一等公民”的存在,也就意味着,我们可以和使用 int 以及 String 一样,将函数当做 参数、值、类型来使用。

其中,将函数当作一个参数和值来使用可见下:

typealias  addTwoInts = (Int,Int)->(Int)          var funcType = addTwoInts.self    func aAddb(a:Int,b:Int) -> Int {        return a+b    }        func addFunc(_ add:addTwoInts,_ a:Int,_ b:Int) -> Int {        return add(a,b)    }//调用     self.addFunc(aAddb, 5, 6)       //  print  -->   11

调用函数 “ self.addFunc(aAddb, 5, 6) ” 时候,aAddb就是一个典型的“值”, 尽管它实际上是一个函数。 与此同时, 它还做为addFunc的参数来使用。

虽然这看起来多此一举,但实际这恰恰体现了高阶函数的特点,牺牲一点点代码的简短,将重点体现在逻辑的清晰上。

一、一个高阶函数的例子

我更喜欢叫下面的这个函数为高阶函数:

var names:[String] = ["61","95","8","248","42"]  //一个包含字符串的数组     names = names.sorted { (s1, s2) -> Bool in   return s1
["248", "42", "61", "8", "95"]
 

这是一个排序函数。不要在意结果并没有按照数字的大小排序,那是因为这是字符串排序,规则将按照首个字母的asc值进行比较。

先看看这个函数的原型:

public func sorted(by areInIncreasingOrder: (Element, Element) -> Bool) -> [Element]

这个函数显然是将一个  (Element, Element) -> Bool 类型的函数做为他的参数

一眼看过去,并不是那么好理解,来看下其内部的实现大概是这样的: 

extension Array{        typealias  IncreasingOrder = (String,String) -> Bool        mutating func mySorted(_ increasingOrder:IncreasingOrder) -> [String] {        var newString:[String] = [String]()        // 假设这里采用简单选择排序        for n in 0..

调用: 

names = ["61","95","8","248","42"]  names = names.mySorted { (s1, s2) -> Bool in            return s1
["248", "42", "61", "8", "95"]

这里为了简便, 直接将Element替换成了String,针对String 类型来说,这个函数的功能和系统的Sotred的功能是一样的。 如果需要支持更多的类型,可能要使用到泛型,甚至是where的可选绑定。其实系统的排序已经实现可选绑定式的排序了:重载了sorted函数,根据Element的不同类型,推断是否需要进行可选绑定动作。

通过这个例子,可以看到,所谓的高阶函数,其实就是将一个函数做为另一个函数的参数的语法。这个语法的基础是Swift中的特性:函数的一等公民性质。

我们可以通过这种类型的语法,将类似的函数的内部代码实现隐藏,只根据参数函数的值定义如何执行内部代码。这中方式实现的代码的灵活度将大大的提高。这为在Swift 中写出使用一个函数替代多个同质化的函数提供了一种手段。 当然,做到这种效果可能还需要使用泛型编程。

 

函数嵌套

大部分情况下,遇到的所有功能都是全局函数,它们在全局范围内定义。如果在函数局部定义一个函数,则称为嵌套函数。

默认情况下,嵌套函数从外部世界隐藏,但在局部仍然可以正常低调用。一个闭包的函数也可以返回一个嵌套函数,以允许在其他范围内使用嵌套函数。

func chooseStepFunction(backward: Bool) -> (Int) -> Int {    //全局函数    func stepForward(input: Int) -> Int { return input + 1 }     //嵌套函数 1    func stepBackward(input: Int) -> Int { return input - 1 }    //嵌套函数 2    return backward ? stepBackward : stepForward                 //返回一个函数}

调用:

var currentValue = -4let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)// moveNearerToZero now refers to the nested stepForward() functionwhile currentValue != 0 {    print("\(currentValue)... ")     ---->  // -4...  // -3...// -2...// -1...
currentValue = moveNearerToZero(currentValue) } print("zero!") // zero!

嵌套函数的本质是返回一个函数,之前所说的将函数当作参数基于同一个特性 -- 函数是swift的 一等公民。

同样的道理,一个基本类型的既然能够做为局部的变量来存在,函数为什么不行呢? 当然可以。这就是局部函数的由来,看来还是基于一等公民的身份啊。 函数嵌套将遵守与基本类型一样的原则,局部的函数,职能够在局部去访问,在外部是没有效果的。

如果我们将嵌套的函数匿名的话,也即是我们下面的这种形式:

typealias  adds = (Int)->(Int)    func add(_ c:Int) -> adds {        return { a in           return a + c        }  }

调用:

let addStart = self.add(0)let addTwo = addStart(2)let addFive = addStart(5)print(addTwo)         ----->   2print(addFive)        ----->   5

在add函数中,定义了一个起始的数值: 0,返回一个adds类型的函数。 之后我们可以通过给adds类型的实例 addTwo和addFive传递相应的参数即可实现多个函数的套用。 

比如,如果我们在做一个以一个起始值做为加减的时候,这中用法就灵活很多,比如,如果我需要以 10做为初始值:

let add = self.add(10)let addTwo = add(2)let addFive = add(5)print(addTwo)      ==> 12print(addFive)     --> 15

add已经是另一个函数了。

而实际上,这就函数嵌套的另一种使用,这有很多的叫法 ,我更愿意叫它 -- 柯里化。

柯里化

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。

在一般函数中,并不能轻易做到柯里化,可能需要借助其他的方案,比如,将多个参数使用替换成一个struct或者class。

然而高阶函数和嵌套函数却可以很轻易的做到,并保证代码的逻辑清晰。然而为什么要使用柯里化,可以阅读。 私人觉得,柯里化的优势是去同质化的代码 以及 注重函数的实现减少参数的干扰,简洁提升逻辑。上面的例子刚好解释了柯里化的使用。

 

 

 

转载于:https://www.cnblogs.com/FBiOSBlog/p/7593321.html

你可能感兴趣的文章
MHA 代码解析(online swtich+master is alive 模式)
查看>>
利用openssl进行RSA加密解密
查看>>
盒模型--边界
查看>>
14.使用通配符
查看>>
软件的模块化开发
查看>>
腾讯、百度、阿里面试经验—(3)阿里面经
查看>>
稍复杂的ionic例子:显示一个列表,并且允许点击进入列表项
查看>>
Liferay 6开发学习(二十六):数据库连接相关问题
查看>>
【转】半路学编程,可以成为大牛吗?
查看>>
【20170506】贝业新兄弟IT总监李济宏:第三方家居物流的IT架构探索
查看>>
【Excle数据透视】如何在数据透视表字段列表中显示更多的字段
查看>>
vue 记一次编译没反应、无进度、没有任何报错的提示,但后台却TM一直消耗内存的BUG:...
查看>>
poj3517
查看>>
iphone http下载文件
查看>>
poj 1195:Mobile phones(二维树状数组,矩阵求和)
查看>>
Codeforces 433 C. Ryouko's Memory Note
查看>>
java中的Static class
查看>>
实例讲解Linux下的makefile
查看>>
json lib 2.4及其依赖包下载
查看>>
计算机中文核心期刊
查看>>