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

最後修改日期為 OCT 6, 2017

前言

控制結構 (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 Yahoo
追蹤本站
Facebook Facebook Twitter