編程挑戰:你是Swift忍者嗎?
var a = "Marin" var b = "Todorov" var myTuple = (a, b)
var name: String var family: String (name, family) = ("Marin", "Todorov")
<div>
</div>
<div>
合并你在前面兩個例子中學到的,你現在可以寫一個函數,獲取兩個任意類型(它們是同一種數據類型)的變量并交換它們的值。下面是解決原來問題的代碼,和在 playground 中 r 的測試代碼:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">func swap<T>(inout a:T, inout with b:T) {
(a, b) = (b, a) }
//demo code var a = "Marin", b = "Todorov" swap(&a, &b)
[a, b] </pre>
<div>
你定義一個函數 swap(a:, with:),包含兩個可讀寫的同種類型T的參數。
</div>
<div>
</div>
<div>
在函數中的單行代碼,你只需要根據函數的兩個參數創建一個元組,然后把值賦給另外一個元組。然后你改變這兩個參數的順序來交換元組的值。
</div>
<div>
</div>
<div>
上面的代碼也聲明了兩個變量a和b來展示如何使用swap(a:, with:)。最后一行代碼將在窗口的右手邊的playground輸出區域輸出a和b的值,結果如下:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">["Todorov", "Marin"] </pre>
<p> </p>
<div>
如果你在 Playground 中運行了代碼,學會了如何用元組交換值,給你自己另外一個
<a href="/misc/goto?guid=4958962951818984014"><img class=" size-full wp-image-473 alignnone" src="https://simg.open-open.com/show/4002b96f4b6e87c4a40ce4f6d213bd2a.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="24" /></a>!
</div>
<div>
</div>
<div>
<strong>挑戰 #2</strong>
</div>
<div>
Swift函數是非常靈活的 — 它們可以接受可變數量的參數,返回一個或多個值,返回其他的函數等等。
</div>
<div>
</div>
<div>
在這次的挑戰中,將測試你對 Swift 函數的語法的理解。寫一個滿足下列要求的,命名為 flexStrings 的函數:
</div>
<div>
</div>
<div>
1. 函數必須接受0,1或2個字符串參數。
</div>
<div>
2. 返回函數參數連接起來的字符串。
</div>
<div>
3. 如果沒有參數傳遞給函數,它將返回字符串“none”。
</div>
<div>
4. 函數體只使用一行代碼可獲得一個額外的飛鏢extrashuriken。
</div>
<div>
</div>
<div>
下面是一些調用函數的示例和輸出:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">flexStrings() //--> "none"
flexStrings(s1: "One") //--> "One" flexStrings(s1: "One", s2: "Two") //--> "OneTwo" </pre>
<div>
解決問題將獲得
<a href="/misc/goto?guid=4958962951587467429"><img class=" size-full wp-image-478 alignnone" src="https://simg.open-open.com/show/c43f3a6fc3f958c1fd2d6fd859a736d5.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="68" /></a>,如果你用一行代碼完成了它并且完成了上面4個要求,你將獲得一個額外的飛鏢。
</div>
<div>
</div>
<div>
解決方法:提示
</div>
<div>
Swift 函數參數可以有默認值,因此你調用函數時,可以省略參數。
</div>
<div>
</div>
<div>
記得現在在這次挑戰中你只能獲得
<a href="/misc/goto?guid=4958962951704480234"><img class=" size-full wp-image-472 alignnone" src="https://simg.open-open.com/show/b0ffa37ee5f9afed3afce7a085a776a2.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="46" /></a>了 — 而且一行代碼解決問題也沒有額外的飛鏢了!
</div>
<div>
</div>
<div>
解決方法:教程
</div>
<div>
Swift 函數參數可以有一個默認值,這是舊的 Objective-C 和 Swift 之間的一個差別。當參數有默認值時,你可以用它的名稱調用這個函數。
</div>
<div>
</div>
<div>
好處就在于如果你喜歡用它的默認值,你可以忽略參數。真棒!
</div>
<div>
</div>
<div>
解決方案?當你知道參數默認值后,它是如此的簡單:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">func flexStrings(s1: String = "", s2: String = "") -> String {
return s1 + s2 == "" ? "none": s1 + s2 } </pre>
<div>
你定義了一個接受兩個參數的函數,但是它們的默認值都為“”(空字符串)。你可以調用這個函數用如下方法:
</div>
<div>
</div>
<div>
1. 正常的2的參數。
</div>
<div>
2. 你傳遞1個參數,另外一個參數是默認值“”。
</div>
<div>
3. 沒有參數,函數將都使用默認值。
</div>
<div>
</div>
<div>
所以在函數體里,你只需要檢查是否兩個參數都是空字符串 (s1+s2 == “”),如果是就返回 “none” ,如果不是,就連接 s1 和 s2 然后返回結果。看!所以要求都滿足了。
</div>
<div>
</div>
<div>
完成一行代碼的要求,你可以看到 Objective-C 的三元運算符?:也在 Swift 重現了。
</div>
<div>
</div>
<div>
試著在 Playground 中使用不同的參數調用這個函數 — 確定你理解了它是如何工作的。做完后給你自己一個
<a href="/misc/goto?guid=4958962951818984014"><img class=" size-full wp-image-473 alignnone" src="https://simg.open-open.com/show/4002b96f4b6e87c4a40ce4f6d213bd2a.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="24" /></a>。
</div>
<div>
</div>
<div>
<strong>挑戰 #3</strong>
</div>
<div>
你已經在前面的挑戰中掌握了使用可選參數的函數。相當有趣吧!
</div>
<div>
</div>
<div>
但是根據之前的方法,你只能有一個固定最大數量的參數。如果你想真的得到一個可變數量的輸入參數的函數,這還有一個更好的方法。
</div>
<div>
</div>
<div>
這次挑戰展示了如果更好的使用內建的 Array 方法和 switch 語句。你在讀 Apple 的
<a target="_blank">Swift Programming Language</a> 書時注意過嗎?那里面講過。:]
</div>
<div>
</div>
<div>
寫一個命名為 sumAny 的函數,它接受任何類型的0或更多的參數。函數應滿足如下要求:
</div>
<div>
</div>
<div>
1. 這個函數將根據下面的規則用String返回傳遞過來的參數值的和。
</div>
<div>
2. 如果參數是一個空的字符串或一個數值0,結果添加-10。
</div>
<div>
3. 如果參數是一個正數的字符串(比如“10″,“-5″則不符合),添加到結果中。
</div>
<div>
4. 如果參數是一個Int值,添加到結果中。
</div>
<div>
5. 如果是其他值,則忽略,不添加到結果中。
</div>
<div>
</div>
<div>
extrashuriken 額外的飛鏢 – 函數使用一個return語句而且不使用任何循環(比如 for 或 while)。
</div>
<div>
</div>
<div>
這里是一些調用函數的示例和結果,你可以完成后檢查你的函數:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">let resultEmpty = sumAny() //--> "0"
let result1 = sumAny(Double(), 10, "-10", 2) //--> "12" let result2 = sumAny("Marin Todorov", 2, 22, "-3", "10", "", 0, 33, -5) //--> "42" </pre>
<div>
<a href="/misc/goto?guid=4958962951587467429"><img class=" size-full wp-image-478 alignnone" src="https://simg.open-open.com/show/c43f3a6fc3f958c1fd2d6fd859a736d5.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="68" /></a>
</div>
<div>
</div>
<div>
解決方法:提示
</div>
<div>
你可以把函數的最后一個參數定義為name: Type…,這樣就定義一個接受可變數量參數的函數。然后,你就可以用一個普通的Array來操作name了。
</div>
<div>
</div>
<div>
你可以用Array.map((T)->(T))來一個一個處理其中的元素。也可以用Array.reduce(T, (T)->(T))換算數組中的單個值。
</div>
<div>
</div>
<div>
最后,計算數字的和,然后在返回結果前轉換為String。祝你好運!
</div>
<div>
</div>
<div>
<a href="/misc/goto?guid=4958962951704480234"><img class=" size-full wp-image-472 alignnone" src="https://simg.open-open.com/show/b0ffa37ee5f9afed3afce7a085a776a2.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="46" /></a>
</div>
<div>
</div>
<div>
解決方法:教程
</div>
<div>
這個問題涉及到了很多 Swift 內建的語言特性,所以在看最終的解決方案之前,讓我們先來瀏覽一些概念。
</div>
<div>
</div>
<div>
首先來看一下如何定義一個接受可變數量參數的函數:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">func sumAny(anys: Any...) -> String {
//code } </pre>
<div>
如果你在函數的參數后面加上 “…”,Swift 將接受 0 到多個這種類型的參數。當你指定 Any…時,它意味著任何數量的任何類型的值可以傳給這個函數。
</div>
<div>
</div>
<div>
你可以在函數中把上面例子中的anys當做一個普通的數組來對待。比如:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">func sumAny(anys: Any...) -> String {
return String(anys.count) } </pre>
<div>
上面的代碼返回了數組 anys 元素的數量,它也是傳給函數的參數的數量。
</div>
<div>
</div>
<div>
下一步,你需要計算這些值的和了。你可以使用一個循環來遍歷數組的值,然后用一個臨時變量保存結果。但是用這種解決方案你就得不到額外的飛鏢了 :]
</div>
<div>
</div>
<div>
你將使用一個不同的策略 — 使用 Array.map((T)->(T)) 來(按要求)轉化所有元素為Int值,然后使用 Array.reduce(T, (T)->(T)) 來合計所有的值。
</div>
<div>
</div>
<div>
這里是轉換每一個元素到需要合計的值的代碼:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">anys.map({item in
switch item { case "" as String, 0 as Int: return -10 case let s as String where s.toInt() > 0: return s.toInt()! case is Int: return item as Int default: return 0 } }) </pre>
<div>
map 迭代了數組中的每一個元素,然后閉包的函數處理它。改變元素或返回任何你想要的值是所有的選項。在上面閉包的函數中,你只添加了一個 switch 語句然后按指定的要求轉換了每一個元素。
</div>
<div>
</div>
<div>
如果你不熟悉 Swift 中的 switch 進階用法,這里有每一種用法的說明:
</div>
<div>
</div>
<div>
1. 如果是一個空字符串“”或0,然后轉換這種元素為值-10。
</div>
<div>
2. 如果是一個字符串元素,而且轉換為Int值后大于0 – 轉換這種元素為它們的 Int 值。
</div>
<div>
3. 如果是Int值返回它的值。
</div>
<div>
4. 所有不匹配上面條件的數組中的其他元素將被轉換為0。
</div>
<div>
</div>
<div>
好的!這個switch語句把你的隨機類型的值全部轉換成了整數。在你調用 map 之后,你實際上已經用一個 Int 數組替換了一個任何類型的數組。
</div>
<div>
</div>
<div>
獲取數組元素的和是 Array.reduce(T, (T)->(T)) 的完美應用。讓我們看一下下面的示例中的函數是如何動作的:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">[1,2,3].reduce(4) { result, item in
return result + item } //--> 10 </pre>
<div>
1. 你定義了一個 Array,包含元素 1, 2 和 3。
</div>
<div>
2. 然后你用4做為起始值調用了reduce。
</div>
<div>
3. reduce 用 4(result) 和第一個元素 1(item) 調用了給定的閉包函數,閉包函數處理結果為 5。
</div>
<div>
4. 接下來,reduce 用結果 5(之前的結果)和下一個數組中的元素(2)調用了閉包函數。
</div>
<div>
5. 這次的結果是 7,所以當 reduce 用最后一個數組調用完函數然后添加 3 – 最終結果則變為了10。
</div>
<div>
</div>
<div>
<strong>reduce</strong>是一個非常方便的函數 — 你不需要聲明數組或變量,代碼也看起來干凈多了。
</div>
<div>
</div>
<div>
現在合并上面討論的技術,產生了最終的解決方案:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">func sumAny(anys: Any...) -> String {
return String((anys.map({item in switch item { case "" as String, 0 as Int: return -10 case let s as String where s.toInt() > 0: return s.toInt()! case is Int: return item as Int default: return 0 } }) as [Int]).reduce(0) { $0 + $1 }) } </pre>
<div>
這是怎么工作的,下面將一點一點說明:
</div>
<div>
</div>
<div>
1. 你使用 map 轉換所有元素為它們需要合計的值。
</div>
<div>
2. 你轉換 map 的結果為 Int[] ,然后所有元素變為整數。
</div>
<div>
3. 你使用示例中的 reduce 合計了所有的元素。
</div>
<div>
4. 最后,你在返回之前把結果轉換為 String。
</div>
<div>
</div>
<div>
你不需要使用任何循環或任何變量 — 只需要使用 Array 的內建函數。酷吧?
</div>
<div>
</div>
<div>
如果你學會并掌握了如何使用 map 和 reduce,給你自己1個飛鏢。
</div>
<div>
</div>
<div>
<a href="/misc/goto?guid=4958962951818984014"><img class=" size-full wp-image-473 alignnone" src="https://simg.open-open.com/show/4002b96f4b6e87c4a40ce4f6d213bd2a.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="24" /></a>
</div>
<div>
</div>
<div>
<strong>挑戰 #4</strong>
</div>
<div>
寫一個命名為 countFrom(from:Int, #to:Int) 的函數,它將輸出(比如通過 print() 或 println() )從 from 到 to 的數值。你不可以使用任何循環、變量或者任何內建的數組函數。假定 from 的值小于to(輸入是有效的)。
</div>
<div>
</div>
<div>
下面是調用的示例和它的輸出:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">countFrom(1, to: 5) //--> 12345 </pre>
<p> </p>
<div>
<a href="/misc/goto?guid=4958962951587467429"><img class=" size-full wp-image-478 alignnone" src="https://simg.open-open.com/show/c43f3a6fc3f958c1fd2d6fd859a736d5.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="68" /></a>
</div>
<div>
</div>
<div>
解決方法:提示
</div>
<div>
使用遞歸函數,每次調用增加 from 的值,直到它與to參數的值相等。
</div>
<div>
</div>
<div>
<a href="/misc/goto?guid=4958962951704480234"><img class=" size-full wp-image-472 alignnone" src="https://simg.open-open.com/show/b0ffa37ee5f9afed3afce7a085a776a2.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="46" /></a>
</div>
<div>
</div>
<div>
解決方法:教程
</div>
<div>
本問題的解決方案將涉及遞歸。對于每一個從 from 開始的數值,你將遞歸調用 countFrom 同時每次給 from 的值增加1。當 from 與 to 相等時,你將停止遞歸。這將有效的把函數轉換成一個簡單的循環。
</div>
<div>
</div>
<div>
下面我們來看一下完成的解決方案:
<p> <br />
</p>
<pre class="brush:swift; toolbar: true; auto-links: false;">func countFrom(from: Int, #to: Int) {
print(from) // output to the assistant editor if from < to { countFrom(from + 1, to: to) } }
countFrom(1, to: 5) </pre>
<div>
當你調用函數時,它打印 from 參數“1”然后把它與to對比。1小于5,所以函數繼續遞歸調用它自己 countFrom(1 + 1, 5)。
</div>
<div>
</div>
<div>
第二次調用輸出“2”然后對比2和5,然后再次調用它自己:countFrom(2 + 1, 5)。
</div>
<div>
</div>
<div>
函數將會繼續遞歸調用它自己并增加 from 的值,直到 from 等于5,遞歸才會停止。
</div>
<div>
</div>
<div>
遞歸是一個強大的概念,接下來的問題將會需要遞歸,所以確保你理解了上面的解決方案!
</div>
<div>
</div>
<div>
為你學習了如何在Swift中使用遞歸給你自己
<a href="/misc/goto?guid=4958962951818984014"><img class=" size-full wp-image-473 alignnone" src="https://simg.open-open.com/show/4002b96f4b6e87c4a40ce4f6d213bd2a.png" alt="編程挑戰:你是Swift忍者嗎?" height="24" width="24" /></a>。
</div>
<div>
</div>
<hr />
<div>
<strong>下一步我們將去哪?</strong>
</div>
<div>
</div>
<div>
<a href="/misc/goto?guid=4958962952191736492"><img class=" size-full wp-image-475 alignnone" src="https://simg.open-open.com/show/dc89ef527241eb47bc548b00ea5d6752.png" alt="編程挑戰:你是Swift忍者嗎?" height="250" width="250" /></a>
</div>
<div>
</div>
<div>
在第2節中得到復仇!
</div>
<div>
</div>
<div>
忍者也需要休息。如果你跟著本教程到了這里,你已經完成了很好的工作 — 你應該休息一下了!
</div>
<div>
</div>
<div>
我想利用這個時間來考慮一下前面的問題和解決方案。Swift非常強大,而且比Objective-C更具表達性和安全性。
</div>
<div>
</div>
<div>
隨著這次挑戰中的最初4個問題,我想讓你在Swift中的各種領域中瀏覽一下。我希望最初的幾個問題到目前為止很有趣和你也得到了有益的經驗。
</div>
<div>
</div>
<div>
讓我們做個小總結:
</div>
<div>
1. 到現在為止,沒有一個解決方案需要你去聲明任何的變量 — 你可能比你想像中更不需要它們!
</div>
<div>
2. 你也沒有使用任何的循環。反思一下!
</div>
<div>
3. 具有默認值的參數和可變數量的參數讓 Swift 的函數異常強大。
</div>
<div>
4. 了解強大的基本內建函數也是非常重要的,比如 map, reduce, sort, countElements 等等。
</div>
<div>
</div>
<div>
如果你真的想放下編程,你可以看看開放的經典游戲忍者龍劍傳的片段:
</div>
<div>
<video id="video-7303-1" class="wp-video-shortcode" preload="metadata" controls="controls" height="480" width="640"> <source type="video/mp4" src="http://www.tairan.com/wp-content/uploads/2014/07/Nes-Ninja-Gaiden-Intro-油Tube_1.mp4?_=1"> <a href="/misc/goto?guid=4958962952294843883">http://www.tairan.com/wp-content/uploads/2014/07/Nes-Ninja-Gaiden-Intro-油Tube_1.mp4</a> </video>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
轉載請注明:Swift中文網 ? 編程挑戰:你是Swift忍者嗎?