原文作者:Erica Sadun
原文鏈接:http://ericasadun.com
時(shí)間:2016年7月19日
譯者:與狼同行
最近幾天我從Swift-Ev看到一個(gè)十分有趣的請(qǐng)求捍歪。
某位來自Swift-Ev社區(qū)成員:當(dāng)我敲代碼的時(shí)候,我很多次都會(huì)遇到這樣一個(gè)情型,當(dāng)使用‘repeat...while’循環(huán)時(shí),循環(huán)條件依賴于那些在循環(huán)體中被聲明變量迂卢。
repeat {
let success = doSomething()
} while !success
這個(gè)請(qǐng)求吸引我的地方是它既需要范圍內(nèi)可見的狀態(tài)(success)而且還需要這個(gè)控制語句的狀態(tài)(success)沒有在外部范圍內(nèi)被聲明段只。
不用擔(dān)心,其實(shí)這個(gè)機(jī)制已經(jīng)在Swift中存在,來看看Swift里新的sequence函數(shù)奢浑。Swift提供了2個(gè)種類,兩個(gè)方式都提供基于循環(huán)域來建立狀態(tài)。
1.public func sequence<T>(first: T, next: (T) -> T?) -> UnfoldSequence<T, (T?, Bool)>
譯者解讀:
該函數(shù)功能為返回從以first”作為開始元素到不斷被"next"先前元素所返回的結(jié)果的一個(gè)隊(duì)列。
例如:
// 我們可以來走一遍樹的元素,從一個(gè)節(jié)點(diǎn)到它的根元素
for node in sequence(first: leaf, next: { $0.parent }) {
node是葉子節(jié)點(diǎn), 然后是它的父節(jié)點(diǎn), 再然后是它的父節(jié)點(diǎn)的父節(jié)點(diǎn), etc.
}
public func sequence<T>(first: T, next: (T) -> T?) -> UnfoldFirstSequence<T> {
// 源碼中其實(shí)調(diào)用了第二種函數(shù),可以看出隊(duì)列第一個(gè)first的元組是true,則直接輸出第一個(gè)first元素,然后接下來隊(duì)列中的元素都執(zhí)行next后返回結(jié)果,直到最后結(jié)果為nil
return sequence(state: (first, true), next: { (state: inout (T?, Bool)) -> T? in
switch state {
case (let value, true):
state.1 = false
return value
case (let value?, _):
let nextValue = next(value)
state.0 = nextValue
return nextValue
case (nil, _):
return nil
}
})
}
2.public func sequence<T, State>(state: State, next: (inout State) -> T?) -> UnfoldSequence<T, State>
譯者解讀:
源碼為:
public func sequence<T, State>(state: State, next: (inout State) -> T?)
-> UnfoldSequence<T, State> {
return UnfoldSequence(_state: state, _next: next)
//這里調(diào)用了另外一個(gè)結(jié)構(gòu)體UnfoldSequence,名為展開隊(duì)列
}
public struct UnfoldSequence<Element, State> : Sequence, IteratorProtocol {
public mutating func next() -> Element? {
guard !_done else { return nil }
if let elt = _next(&_state) {
//這句可以看出為什么sequence函數(shù)的next的返回類型都是可選類型,
//當(dāng)執(zhí)行next循環(huán)語句返回nil,隊(duì)列剩余部分則不執(zhí)行next函數(shù),全部返回nil
return elt
} else {
_done = true
return nil
}
}
internal init(_state: State, _next: (inout State) -> Element?) {
self._state = _state
self._next = _next
}
internal var _state: State
internal let _next: (inout State) -> Element?
internal var _done = false
}
兩者之間的不同之處在于,第一個(gè)更簡單的函數(shù)產(chǎn)生一個(gè)和它狀態(tài)相同類型的sequence序列。而第二個(gè)函數(shù)則將狀態(tài)類型和輸出類型區(qū)分了出來,因此你可以生成整數(shù),并且同時(shí)對(duì)字符串做出處理亚亲。
現(xiàn)在你來仔細(xì)想一想,是不是發(fā)現(xiàn)repeat-while真是就像是一個(gè)另外一種形式的sequence序列。下面我們來舉一個(gè)簡單的例子腐缤。
var i = 0
repeat {
print(i) // some loop body
i = i + 5
} while i <= 100
現(xiàn)在你看了上述的內(nèi)容,你可以進(jìn)行重寫,來試著把i狀態(tài)變量納入控制結(jié)構(gòu),就像這樣:
for i in sequence(first: 0, next: { $0 + 5 }) {
print (i) // some loop body
if i >= 100 { break }
}
或者你可以更大膽一些,試著把所有的行為和狀態(tài)寫入next閉包中,就像這樣:
for _ in sequence(first: 0, next: {
print($0) // some loop body
let value = $0 + 5
return value <= 100 ? value : nil
}) {}
這里有3件比較重要的事關(guān)于這項(xiàng)第三種寫法:
1.這個(gè)for循環(huán)不需要變量捌归。它只是被用于去執(zhí)行這個(gè)序列。
2.這個(gè)循環(huán)體是空的,它僅僅被用于完成這個(gè)語法岭粤。當(dāng)然你也可以去執(zhí)行數(shù)組(也算一個(gè)序列),但那樣將會(huì)需要申請(qǐng)內(nèi)存,那個(gè)做法是十分浪費(fèi)的惜索。
3.這個(gè)序列當(dāng)返回nil時(shí)會(huì)終止。這就意味著這個(gè)閉包的返回類型是T剃浇?,而T則是第一個(gè)參數(shù)的類型巾兆。在這個(gè)例子中return的值只可以返回?cái)?shù)字類型,因?yàn)檫@個(gè)值并沒有有意義的用途,它只是被用于檢查false/nil猎物。
如果你想結(jié)合Bool條件,有一種快速的方法,可以把Bool類型轉(zhuǎn)化為一個(gè)可選。雖然這樣做有些過了,但它的確能完成任務(wù)角塑。
extension Bool { var opt: Bool? { return self ? self : nil } }
或者,你也可以寫一個(gè)函數(shù)來處理Boolean作為控制語句,這樣你就不需要轉(zhuǎn)換Boolean作為可選蔫磨。
這個(gè)perform
函數(shù)是用于創(chuàng)造一個(gè)狀態(tài)化的repeat-while循環(huán),它使用一個(gè)Boolean作為控制,封裝了sequence函數(shù)的使用,并且允許這個(gè)循環(huán)體是一個(gè)尾隨閉包。
func perform<T>(
with state: T,
while test: (T) -> Bool,
repeat body: (inout T) -> Void) -> T {
var updatedState: T = state
let boolSequence = sequence(state: state, next: {
(state: inout T) -> Bool? in
body(&state)
updatedState = state
return test(updatedState) ? true : nil
})
for _ in boolSequence {}
return updatedState
}
// 下面的示例將這些單詞連接到一個(gè)空格分隔的字符串中圃伶。
let joinedWords = perform(
with: ["Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit"],
while: { $0.count > 1 })
{
(state: inout [String]) in
guard state.count >= 2 else { return }
let (last, butLast) = (state.removeLast(), state.removeLast())
let joinedLast = butLast + " " + last
state.append(joinedLast)
}.first!
debugPrint(joinedWords)
輸出結(jié)果為:"Lorem ipsum dolor sit amet consectetur adipiscing elit"
這里最關(guān)鍵的地方是初始的單詞數(shù)組(指["Lorem", "ipsum"...])并沒有被存儲(chǔ)在循環(huán)外部的任何地方,但是可以在循環(huán)體內(nèi)操作堤如。
我相信這個(gè)特性正符合社區(qū)成員所說的"當(dāng)使用‘repeat...while’循環(huán)時(shí),循環(huán)條件依賴于那些在循環(huán)體中被聲明變量"這句話。