[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;
        }
    }
    
    【分享文章】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤網站】
    Facebook Facebook Twitter Plurk
    【支持本站】
    Buy me a coffeeBuy me a coffee