巢狀結構

還記得第一週講解迴圈時,我們曾經介紹過巢狀迴圈(迴圈中有另一個迴圈)這個技巧嗎?藉由巢狀結構,我們可以簡化大量的程式碼,不只是對迴圈這種控制指令的語法如此,對陣列這種控制資料的語法,也同樣如此!

考慮一個設計需求:我們要撰寫一個基本的3x3圈圈叉叉小遊戲,為了記憶每個方格內目前是畫了圈圈還是叉叉,並且顯示在螢幕上給使用者看,必須設計一個資料結構。我們決定使用一個整數陣列來記錄這些方格的狀態,0代表空格,1代表叉叉,2代表圈圈,並且使用for-in迴圈做顯示的工作,用switch-case處理整數轉為圖案的工作:

var 方格 = [0,0,0,0,0,0,0,0,0] 

for i in 1...3 {  
    for j in 1...3 {
        // 列印方格狀態的程式碼?
    }
}

之所以會寫成巢狀迴圈,是因為圈圈叉叉的遊戲板塊是一個長為3、寬為3的矩形,因此我們列印三個方格之後才要換一行,而列印每一行必然只伴隨一次print()指令,所以我們撰寫程式前初步的線索是:print()這個指令將會進行三次。

為了將問題簡化,我們不妨先印出前三個方格就好了...

var line = "" // 用來記憶一行狀態的字串,初始為空白字串

for j in 1...3 {
    switch 方格[j-1] { // 記得,陣列的索引由0開始,這邊把1...3轉換成0...2
        case 0:
            line = line + " " // 0代表空格
        case 1:
            line = line + "×" // 1代表叉叉
        case 2:
            line = line + "o" // 2代表圈圈
        default:
            print("應該不可能出現0,1,2以外的數值才對...")
    }
}

print(line) // 印出一行

藉由取得索引為0,1,2的陣列元素,我們可以成功印出遊戲盤上第一行的方格狀態,以此類推的話,我們只要將目前的程式碼包在第二層迴圈裡面,也執行三次,就應該能印出全部三行的方格狀態了。

var 方格 = [0,1,0,2,1,2,0,1,2] 
var line:String

for i in 1...3 {
    line = "" // 用來記憶一行狀態的字串,畫每行之前先清除

    for j in 1...3 {
        switch 方格[ (j-1) + (i-1)*3 ] { // 每一次外層迴圈分別會計算出..[0,1,2], [3,4,5], [6,7,8]的結果
            case 0:
                line = line + " " // 0代表空格
            case 1:
                line = line + "×" // 1代表叉叉
            case 2:
                line = line + "o" // 2代表圈圈
            default:
                print("應該不可能出現0,1,2以外的數值才對...")
        }
    }

    print(line) // 印出一行
}

果然!完全正確的執行,但你是否覺得方格[ (j-1) + (i-1)*3 ]這樣的語法很礙眼?而且在撰寫這些算式的時候,腦筋稍微不清楚一點就會造成錯誤(比方說,不小心把i-1寫成i,跑到i=3, j=1這圈的時候,計算結果是9,而電腦是找不到索引為9的方格的,因為圈圈叉叉的遊戲版只需要08的索引。)

我們在想要取得某一方格內的狀態時,其實心中的想像是「我要取得第i欄、第j列的元素」,而目前的寫法方格[k]其實意義是「我要取得第k個元素」,因此有了ij的乘法來算出k這個多此一舉的轉換。這時我們不禁想像...

var 方格 = ?
print(方格[1][2])

有這種語法就太好了是吧,實際上是有的!就如同巢狀for-in迴圈是怎麼構成的一樣,我們也可以回想一下陣列的意義:它是一系列同類型元素的集合,這些元素可以是IntFloatString,甚至是自訂的struct 角色,那麼可不可能某個陣列,是一系列陣列的集合呢?

var 一個整數的陣列 = [1,2,3]
var 一個陣列的陣列 = [陣列1, 陣列2, 陣列3]

產生這種想法之後,很快就會覺得巢狀陣列也不是那麼複雜了(吧?)

var 方格 = [ [0,0,0], [1,1,1], [2,2,2] ]

print(方格[0])    // 方格[0]是一個整數陣列 [0,0,0]
print(方格[0][1]) // 先取得方格[0]這個陣列,接著印出其索引為1的元素

使用巢狀陣列配上巢狀迴圈,並且把for-in迴圈改成0...2來索引,程式碼瞬間就好讀多了!

var 方格 = [ [0,1,0] , [2,1,2] , [0,1,2] ]
var line:String

for i in 0...2 {
    line = "" // 用來記憶一行狀態的字串,畫每行之前先清除

    for j in 0...2 {
        switch 方格[i][j] {
            case 0:
                line = line + " " // 0代表空格
            case 1:
                line = line + "×" // 1代表叉叉
            case 2:
                line = line + "o" // 2代表圈圈
            default:
                print("應該不可能出現0,1,2以外的數值才對...")
        }
    }

    print(line) // 印出一行
}

results matching ""

    No results matching ""