開源技術教學文件網 使用 Groovy 的控制結構 (Control Structures)

最後修改日期為 JAN 27, 2019

控制結構 (control structures) 是用來改變程式運行的順序,讓程式有一些基本的判斷功能。控制結構包括選擇 (selection) 和迭代 (iteration) 兩種;本文介紹 Groovy 中可用的控制結構。Groovy 官網設明文件並未強調控制結構的使用,不過 Groovy 的官方教材 Groovy In Action, 2nd Edition, Manning 中的確有提到一些相關的內容。本文由筆者自行整理而成。

if

if 是最基礎的選擇結構,Groovy 承襲 Java,if 的使用方法也相同。if 的虛擬碼如下:

if (condition) {
    // Run code here.
}

condition 為真時,會執行 if 區塊內的程式碼。

我們還可以加上反向的 else 敘述,其虛擬碼如下:

if (condition) {
    // Run code here if `condition` is true
}
else {
    // Run code here if `condition` is false
}

condition 為真時,執行 if 區塊內的程式碼。反之,則執行 else 區塊內的程式碼。

除了二元條件外,還可以加上多個 else if 區塊,就可以形成多元條件敘述,參考以下虛擬碼:

if (condition_1) {
    // Do something_1
} else if (condition_2) {
    // Do something_2
} else {
    // Do something else
}

以本例來說,若符合 condition_1 時,程式會執行 something_1,然後跳出整個 if 區塊。若 condition_1 不符時,程式會試著檢查 condition_2,若符合 condition_2,程式會執行 something_2 後跳出整個區塊。若上述條件皆不符合,程式會執行 else 區塊內的內容。

if 敘述中,if 是必需的,而 else ifelse 是選擇性的。else if 可以一至多個,而 else 只能有一個,且要放在最後一個區塊。從語義上去想即可,不用死背這個規則。

以下是簡短的範例:

final random = new Random()                   /*  1 */
final n = Math.abs(random.nextInt() % 3) - 1  /*  2 */

if (n > 0) {                                  /*  3 */
    println "n is larger than zero"           /*  4 */
} else if (n < 0) {                           /*  5 */
    println "n is smaller than zero"          /*  6 */
} else {                                      /*  7 */
    println "n is equal to zero"              /*  8 */
}                                             /*  9 */

我們在第 1 行建立一個 random 物件。接著,在第 2 行中,從 random 物件產生一個介於 -11 之間的隨機整數 n

第 3 行至第 9 行是一個 if 敘述,由 n 的值來印出不同的訊息。

switch

switch 算是 if 的語法糖,在 C 家族的語言都會放。其虛擬碼如下:

switch (value) {
case a:
    // Do something_a
    break
case b:
    // Do something_b
    break
case c:
    // Do something_c
    break
default:
    // Do something else
}

同樣也是會依序由上而下檢查,符合特定區塊時則進入該區塊。

switch 的雷就是會忘了放 break,敘述就會繼續執行下去,這個特性稱為 fallthrough。Groovy 為了保留 Java 的習慣,並沒有改掉這項特性,程式設計者需自行注意。

參考範例如下:

final now = new Date()                     /*  1 */
final c = Calendar.getInstance();          /*  2 */
c.setTime(now);                            /*  3 */
final day = c.get(Calendar.DAY_OF_WEEK);   /*  4 */

switch (day) {                             /*  5 */
    case 6:                                /*  6 */
    case 7:                                /*  7 */
        println "Weekend"                  /*  8 */
        break                              /*  9 */
    case 5:                                /* 10 */
        println "Thanks God. It's Friday"  /* 11 */
        break                              /* 12 */
    case 3:                                /* 13 */
        println "Hump day"                 /* 14 */
        break                              /* 15 */
    default:                               /* 16 */
        println "Week"                     /* 17 */
        break                              /* 18 */
}                                          /* 19 */

我們在第 1 行建立 now 物件,代表程式運行時的的系統時間時間。

接著,在第 2 行中取得 Calendar 物件 c。因為一個程式只需單一的 Calendar 物件,故用 singleton 模式取得唯一的物件 c

在第 3 行和第 4 行中,從 Calendar 物件 c 中使用相關的函式從 now 物件中取得 day (day of week)。

第 5 行至第 19 行是一個 switch 敘述,該敘述根據 day 的狀態來印出相對應的訊息。

switchif 可代換,讀者可自行嘗試將本節範例用 if 改寫。

while

while 是基本的迴圈 (loop) 語法之一,主要用於以條件句為終止條件的迴圈。其虛擬碼如下:

while (condition) {
    // Run code here repeatedly while `condition` is true
}

只要 condition 為真,while 區塊內的程式碼就會重覆執行。

參考簡短範例如下:

def i = 10

while (i > 0) {
    println "Count down ${i}"
    i--
}

以下是無窮迴圈 (infinite loop):

while (true) {
    // Run code here repeated indifintely.
}

如同其名,無窮迴圈內的程式碼區塊會不斷地執行。除了新手偶爾寫錯造成無限迴圈外,實務上我們會用一些中止條件去改變迴圈的行進,詳見下文。

for

for 有多種使用方式,一種是使用計數器 (counter) 來走訪,一種是使用迭代器 (iterator);在 Groovy 中,兩種方式都可以。

傳統的 C 風格 for 使用計數器,參考下例:

for (def i = 10; i > 0; i--) {
    println "Count down ${i}"
}

Groovy 提供 range 語法,可做為迭代器,參考下例:

for (i in 1..10) {
    assert 1 <= i && i <= 10
}

也可以用陣列等容器做為迭代器,詳見後文。

迭代器 (Iterator)

迭代器是指等效於迴圈的語句,主要的好處是不需知道迭代器內部的實作,也不需要使用額外的計數器。Groovy 的迭代器可用容器 (collections) 和 closure 走訪,很大一部分是向 Ruby 致敬。

以下的迭代器指定走訪區塊 3 次:

3.times { println "Hello World" }

以下的迭代器走訪一個 range:

(1..10).each { i -> println i }

以下的迭代器走訪一個陣列,並附帶索引:

["a", "b", "c"].eachWithIndex { e, i ->
    println "${i}: ${e}"
}

一開始寫程式時,通常會不太習慣迭代器,而會想用傳統的 forwhile 迴圈;使用那種方式來迭代其實都可以。一開始可以先用傳統的迴圈來寫,寫一段時間後再慢慢把程式碼改寫 (重構) 成迭代器即可。

continuebreak 改變迴圈行進

continue 可在迴圈進行到中途時,跳過某一次迭代。參考下例:

for (def i = 1; i <= 10; i++) {
    if (i % 2 != 0) {
        continue
    }
    
    println i
}

這個程式在輪到奇時數會跳過剩下的程式碼,故只會印出偶數。

break 則可以中斷迴圈。參考下例:

for (def i = 1; i <= 10; i++) {
    if (i > 5) {
        break
    }
    
    println i
}

這個程式在 i6 時迴圈會中斷,故只印出 15

分享本文
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Yahoo
追蹤本站
Facebook Facebook Twitter