位元詩人 [Raku] 程式設計教學:控制結構 (Control Structure) 或控制流程 (Control Flow)

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

控制結構 (control structure) 或控制流程 (control flow) 用來改變程式運行的方向。可分為兩大類:

  • 選擇 (selection)
  • 迭代 (iteration)

本文會介紹 Raku 中常見的控制結構。

選擇相關的控制結構

if .. elsif .. else

if 是最常見的選擇控制結構。實例如下:

my $n = 0;

if $n > 0 {
    say "n is larger than zero";
} elsif $n < 0 {
    say "n is smaller than zero";
} else {
    say "n is equal to zero";
}

if 也可以用於後位修飾敘述,如下例:

say "Equal" if 1 + 2 == 3;

後位修飾算是 Perl 家族語言的特色之一,對於寫 one liner 很方便。

unless

unlessif 語義相反,條件不符合時才會執行程式碼,因此較少使用。

die "Some error" unless 1.0 + 2.0 - 3.0 == 0.0;

unless 沒有相對應的 elsifelse 區塊。如果覺得 unless 不好用,將條件寫成同義的 if 即可。

with .. orwith .. else

with 可檢查條件是否有定義,算是一種 if 的語法糖。實例如下:

my $food = "seafood";

with $food.index("hamburger") {
    "{$food} is delicious.".say;
} orwith $food.index("chip") {
    "I like {$food} as well.".say;
} else {
    "No favored food".say;
}

without

without 是反向的 with,有點像 unless 對於 if 的關係。實例如下:

# Declare a variable without defining it.
my $answer;

warn "Undefined" without $answer;

同樣地,without 沒有 orwithelse 區塊。

given .. when .. default

given 算是另一種簡化 if 的語法,類似 C 語言的 switch。實例如下:

my $w = Date.today.day-of-week;

given $w {
    when 3 {
        "Hump day".say;
    }
    when 5 {
        "Thank God It's Friday".say;
    }
    when 6..7 {
        "Weekend".say;
    }
    default {
        "Week".say;
    }
}

proceed 和 succeed

proceedsucceed 可以改變 given 區塊的流程。proceed 會繼續前進到下一個 when 區塊,而 succeed 則會跳出整個 given 區塊。以下是實例:

my $w = Date.today.day-of-week;

given $w {
    when 1 {
        proceed;
    }
    when 2 {
        proceed;
    }
    when 4 {
        "Week".say;
    }
    when 3 {
        "Hump day".say;
    }
    when 5 {
        "Thank God It's Friday".say;
    }
    when 6 {
        proceed;
    }
    when 7 {
        "Weekend".say;
    }
    default {
    die "Unknown day";
    }
}

迭代相關的控制結構

一般的程式設計中,將這類控制結構稱為迴圈 (loop)。

while

while 在符合條件的情形下,會無限次執行特定程式,如以下實例:

my $i = 1;

while $i <= 10 {
    "Counting to {$i}".say;
    $i++
}

until

until 是反向的 while,會無限次執行特定程式,直到符合某條件才停止,如以下實例:

my $i = 1;

until $i > 10 {
    "Counting to {$i}".say;
    $i++
}

如果覺得 until 寫起來不直覺,改成相對應的 while 即可。

repeat .. while 或 repeat .. until

加上 repeat 敘述後,即使條件不合,該區塊至少會執行一次。其他用法則和 whileuntil 相同。

my $i = 10;

repeat {
    "Counting to {$i}".say;
    $i++
} until $i > 0;

loop

loop 相當於傳統的 C 風格迴圈。實例如下:

loop (my $i = 0; $i < 10; $i += 2) {
    $i.say;
}

loop 不加上任何計數器,會變無窮迴圈 (infinite loop),如下例:

loop {
    "Hello".say;
}

for

for 搭配迭代器 (iterator) 使用。透過迭代器,不需了解容器內部實作,而可以直接走訪該容器。使用 ... 生成一個 Range 物件,可作為迭代器。如以下實例:

for 1...10 {
  "Counting to {$_}".say;
}

在後續的文章中,我們會將 for 搭配陣列 (array) 或雜湊 (hash) 使用。

改變迴圈行進

next

next 可以跳過目前的迴圈,進入下一次迭代。見以下實例:

loop (my $i = 1; $i <= 10; $i++) {
    if $i % 2 != 0 {
        next;
    }

    $i.say;
}

last

last 會直接離開目前的迴圈。見以下實例:

my $i = 1;

loop {
    if $i > 5 {
        last;
    }

    $i.say;
    $i++;
}

redo

redo 會重新開始同一次迭代。見以下實例:

loop {
    my $x = prompt("Enter a number: ");
    redo unless $x ~~ /\d+/;
    last;
}

用標籤跳躍多層迴圈

nextlastredo 都可以搭配標籤 (labels),可以跳離多層迴圈。

OUTAHERE: while True  {
    for 1,2,3 -> $n {
        last OUTAHERE if $n == 2;
    }
}

實例:終極密碼

這裡用終極密碼這個經典的題目來展示如何使用控制結構。我們先在 1 至 100 間隨機挑出一個數字,再讓玩家猜這個數字。範例如下:

constant $MIN = 1;
constant $MAX = 100;

# Get a random answer between $MIN and $MAX.
my $answer = ($MIN..$MAX).pick;

# Initial program state.
my $guess = -1;
my $upper = $MAX;
my $lower = $MIN;

loop {
    # Get the guess from user.
    loop {
        my $input = prompt("Guess a number between {$lower} and {$upper}: ");

        # Check input is a valid number.
        unless $input ~~ m/ \d+ / {
            $*ERR.say: "Invalid number";
            redo;
        }

        # Check
        unless $lower <= $input and $input <= $upper {
            $*ERR.say: "Invalid range";
            redo;
        }

        # Update the guess.
        $guess = $input;
        last;
    }

    # Check whether the guess is right.
    if $guess == $answer {
        say "You got it";
        last;
    } elsif $guess > $answer {
        say "Too large";
        $upper = $guess;
    } else {
        say "Too small";
        $lower = $guess;
    }
}
關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。