[翻译]Swift编程语言——闭包
闭包闭包是自己自足的功能代码块,能被传递和使用。Swift的闭包和 C语言和OC中的blocks、其他语言中的lambdas 类似。 闭包表达式前面介绍的嵌套函数是在一个大函数中命名和定义的函数。然而有时需要一个简化的版本。这在函数作为参数的时候很必要。 Sorted 函数Swif标准库提供了一个叫做sorted的函数,它用来对已知类型的数组进行排序,实现过程是通过传入的排序闭包。当完成排序操作后,storted函数返回一个和原来数组长度、类型一致的新数组,新数组中的元素已经是经过排序后的了。原始的数组不会被sorted函数修改。 ?let? ?names? = [?"Chris"?,?"Alex"?,?"Ewa"?,?"Barry"?,?"Daniella"?] sorted函数带了两个参数: func? ?backwards?(?s1?: ?String?,?s2?: ?String?) -> ?Bool? { ? ?return? ?s1? > ?s2 ?} ?var? ?reversed? = ?sorted?(?names?,?backwards?) ?// reversed is equal to ["Ewa","Daniella","Chris","Barry","Alex"] 如果第一个字符(s1)比第二个字符串(s2)大,backwards函数返回true,表明在排序后的数组中s1将会出现在s2之前。对于字符串中的字符而言,“大”意味着在字母表中的顺序靠后。这意味着字母B要比字母A大,字符串Tom要比字符串Tim大。这里需要按照字母表倒排序,所以Barry应该在Alex之前。其他的字符串比较也是一样的。 闭包表达式语法闭包表达式语法通常是这样的: { (parameters) -> return type in statements } 闭包表达式语法可以采用常量参数、变量参数、和inout参数。参数的默认值是不能使用的。可变参数可以使用,前提是你给它加了名字而且将其放在参数列表最后。元组当然也可以作为闭包的参数和返回值。 reversed? = ?sorted?(?names?,{ (?s1?: ?String?,?s2?: ?String?) -> ?Bool? ?in ? ?return? ?s1? > ?s2 ?}) 这里内联闭包的参数和返回类型定义,同backwards的定义一样。这两种情下,他们的类型都是 (s1: String,s2: String) -> Bool。然而内联闭包表达式中,参数和返回类型在花括号中被定义,而不是在外部。 reversed? = ?sorted?(?names?,?s2?: ?String?) -> ?Bool? ?in? ?return? ?s1? > ?s2? } ) 这说明,sorted函数的整体调用没有变化。一对参数被包裹在作为整体的参数列表。只不过其中一个参数是一个内联闭包。 从上下文中推断类型因为排序闭包作为参数使用,所以Swift可以根据sorted函数的第二个参数的类型推测出它的参数和返回值类型。第二个参数的类型是(String,String) -> Bool。这就意味着,(String,String)和 Bool 的类型声明在闭包表达式中可以不出现。因为所有的类型都能推测出来,所以->和包围参数的圆括号都可以省略: ?reversed? = ?sorted?(?names?,{ ?s1?,?s2? ?in? ?return? ?s1? > ?s2? } ) 传递一个闭包给一个函数作为内联闭包时,一定能够推断出闭包的所有类型。这样,内联闭包就根本不必要采用完整的写法。 单一表达式闭包省略return上面的例子中,单一表达式闭包定义时可以省略去写return关键字: 参数名的简写对于内联闭包,Swift提供了简写参数名的写法:使用
?reversed? = ?sorted?(?names?,{ ?$0? > ?$1? } ) 这里
操作符函数上例事实上还有一种更简练的写法。Swift的字符串类型支持用>作为一个函数表示大于,返回一个布尔值。这正好符合sorted函数的第二个参数的要求。因此你可以传递大于符号,Swift会推断出你的意图: ?reversed? = ?sorted?(?names?,>) 更多的操作符做函数的信息,参见相关章节。 Trailing 闭包如果你想要传递一个闭包表达式给函数的最后一个参数,同时这个闭包又非常长,这时你可以写一个Trailing 闭包作为替代。一个Trailing 闭包是被写在函数圆括号之外(或之后)的闭包表达式: func? ?someFunctionThatTakesAClosure?(?closure?: () -> ()) { ? ?// function body goes here ?} ? ?// here's how you call this function without using a trailing closure: ? ?someFunctionThatTakesAClosure?({ ? ?// closure's body goes here ?}) ? ?// here's how you call this function with a trailing closure instead: ? ?someFunctionThatTakesAClosure?() { ? ?// trailing closure's body goes here ?} NOTE ?reversed? = ?sorted?(?names?) { ?$0? > ?$1? } 当闭包足够长,不能在一行写完时,Trailing 闭包是非常有用的。有一个例子,Swift的数组类型有一个叫做map的方法,它接受一个闭包作为唯一的参数。对于数组中的每一个元素,都会调用闭包一次,闭包的返回该元素的映射值(或者其他什么的)。具体的映射方式和返回类型由闭包指定。 ?let? ?digitNames? = [ ? ?0?: ?"Zero"?,?1?: ?"One"?,?2?: ?"Two"?,?3?: ?"Three"?,?4?: ?"Four"?,? ?5?: ?"Five"?,?6?: ?"Six"?,?7?: ?"Seven"?,?8?: ?"Eight"?,?9?: ?"Nine" ?] ?let? ?numbers? = [?16?,?58?,?510?] 上面的代码创建了一个用来映射的字典,这个字典关联了整型的数字和对应的英文名字。同时定义了一个整型数组,它将被处理成一个字符串类型的数组。 let? ?strings? = ?numbers?.?map? { ? (?var? ?number?) -> ?String? ?in ? ?var? ?output? = ?"" ? ?while? ?number? > ?0? { ? ?output? = ?digitNames?[?number? % ?10?]! + ?output ? ?number? /= ?10 ? } ? ?return? ?output ?} ?// strings is inferred to be of type [String] ?// its value is ["OneSix","FiveEight","FiveOneZero"] map函数对数组中的每个元素都调用了闭包表达式。你不必指定闭包的参数number的类型,因为它的类型可以从数组的内容类型中推断得到。 捕获值闭包可以在他的定义上下文环境中捕获常量或者变量。闭包体内可以引用和修改这些值,尽管这些常量和变量的原来定义作用域已经不复存在了。 这里有个例子一个叫做makeIncrementor的函数,它里面含有一个嵌套函数叫做incrementor。incrementor函数从它的环境中捕获了两个值,runningTotal 和amount。捕获了这些值后,makeIncrementor 返回incrementor作为闭包(每次调用会给runningTotal加上amount) func? ?makeIncrementor?(?forIncrement? ?amount?: ?Int?) -> () -> ?Int? { ? ?var? ?runningTotal? = ?0 ? ?func? ?incrementor?() -> ?Int? { ? ?runningTotal? += ?amount ? ?return? ?runningTotal ? } ? ?return? ?incrementor ?} makeIncrementor 的返回类型是() -> Int。这意味着它返回一个函数而不是一个简单的值。返回的函数没有返回值,每次被调用时会返回一个 Int值。 func? ?incrementor?() -> ?Int? { ? ?runningTotal? += ?amount ? ?return? ?runningTotal ?} incrementor 函数没有任何参数,但是却可以在其函数体内使用runningTotal 和amount 。这是因为它有从它外部函数中捕获上面两个参数的技能。 let? ?incrementByTen? = ?makeIncrementor?(?forIncrement?: ?10?) 上面给一个叫做incrementByTen的常量赋值一个这样函数(每次调用给runningTotal添加10)。调用它几次后的表现: incrementByTen?() ?// returns a value of 10 ?incrementByTen?() ?// returns a value of 20 ?incrementByTen?() ?// returns a value of 30 如果你创建第二个增加函数,它将有自己的对引用的存储,runningTotal的引用和第一个是不同的: let? ?incrementBySeven? = ?makeIncrementor?(?forIncrement?: ?7?) ?incrementBySeven?() ?// returns a value of 7 再次调用第一个增加函数(incrementByTen),它仍会在自身的runningTotal引用基础上增加,不受incrementBySeven被调用的影响: ?incrementByTen?() ?// returns a value of 40 NOTE 闭包是引用类型上面例子中incrementBySeven 和incrementByTen 是常量,但是这些常量可以对捕获到的runningTotal变量进行累加。这是因为函数和闭包是引用类型的。 ?let? ?alsoIncrementByTen? = ?incrementByTen ?alsoIncrementByTen?() ?// returns a value of 50 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |