Wait the light to fall

Raku 中的 given/when 模式匹配

焉知非鱼

Given When Pattern Match in Raku

模式匹配 #

匹配单个字符串:

my $name = "twostraws";

given $name {
    when "bilbo"      { say "Hello, Bilbo Baggins!"}
    when "twostraws"  { say "Hello, Paul Hudson!"  }
    default           { say "身份验证失败"           }
}

匹配元组:

my $bool1 = 1;
my $bool2 = 0;

given ($bool1, $bool2) {
    when (0, 0) {say "0, 0"}
    when (0, 1) {say "0, 1"}
    when (1, 0) {say "1, 0"}
    when (1, 1) {say "1, 1"}
}

given ("15", "example", "3.14") {
    say $_.WHAT;
    when ($, $, Str) { say "I got a String of $_[2]" }
}

given (4, 5) {
    when ( $,  $) {say "Ok"}
}

given ("fly.mp3", 34, "It's funny") {
    when (/.mp3$/, /4$/, *.chars > 4) { say "Perfact" }
}

given 5 {
    when 1..10 { say "1..10 contains 5" }
}

同时检查名字和密码 #

my $name     = "twostraws";
my $password = "fr0st1es";

given ($name, $password) {
    when ("bilbo", "bagg1n5")      { say "Hello, Bilbo Baggins!" }
    when ("twostraws", "fr0st1es") { say "Hello, Paul Hudson!"   }
    default                        { say "你是谁?"                }
}

使用单个元组 #

my $authentication = ("twostraws", "fr0st1es");

given $authentication {
    when ("bilbo", "bagg1n5")      { say "Hello, Bilbo Baggins!" }
    when ("twostraws", "fr0st1es") { say "Hello, Paul Hudson!"   }
    default                        { say "你是谁?"                }
}

部分匹配 #

# 你只关心某些感兴趣的值,不关心其它值,使用 `*` 号或 `$` 来代表 "any value is fine"
my $authentication = ("twostraws", "fr0st1es", "127.0.0.1");

given $authentication {
    when ("bilbo", "bagg1n5", *)      { say "Hello, Bilbo Baggins!"}
    when ("twostraws", "fr0st1es", $) { say "Hello, Paul Hudson!"  }
    default                           { say "Who are you?"         }
}

只匹配元组的一部分 #

# 但仍然想知道其它部分是什么
my $authentication = ("twostraws", "fr0st1es");

given $authentication {
    when ("bilbo", *)     { say "Hello, Bilbo Baggins!" }
    when ("twostraws", *) { say "Hello, Paul Hudson: your password was $_!" }
    default               { say "Who are you?"         }
}

匹配计算型元组 #

sub fizzbuzz(Int $number) returns Str {
    given ($number % 3 == 0, $number % 5 == 0) {
        when (True, False)  { return "Fizz"     }
        when (False, True)  { return "Buzz"     }
        when (True, True)   { return "FizzBuzz" }
        when (False, False) { return $number.Str}
    }
}

say fizzbuzz(15);

遍历元组 #

my $twostraws = ("twostraws", "fr0st1es");
my $bilbo = ("bilbo", "bagg1n5");
my $taylor = ("taylor", "fr0st1es");
my @users = $twostraws, $bilbo, $taylor;

for @users -> $user {
    say $user[0];
}

使用 when 匹配元组中的指定值 #

my $twostraws = ("twostraws", "fr0st1es");
my $bilbo = ("bilbo", "bagg1n5");
my $taylor = ("taylor", "fr0st1es");
my @users = $twostraws, $bilbo, $taylor;

say "User twostraws has the password fr0st1es" when ("twostraws", "fr0st1es") for @users;

# 打印秘密为指定值的用户
say "User $_[0] has password \"fr0st1es\"" when (*, "fr0st1es") for @users;

匹配范围 #

my $age = 36;

given $age {
    when 0 ..^ 18   { say "你有活力有时间,但是没钱"  }
    when 18 ..^ 70  { say "你有活力有钱,但是没时间"  }
    default         { say "你有时间和金钱,但是没活力"}
}

when 可以配合智能匹配操作符 ~~ 单独使用 #

my $age = 36;
when $age ~~ 0 ..^ 18  { say "你有活力有时间,但是没钱"  }
when $age ~~ 18 ..^ 70 { say "你有活力有钱,但是没时间"  }
default                { say "你有时间和金钱,但是没活力"}

使用 contains 方法 #

my $age = 36;
when (0 ..^ 18).contains($age)  { say "你有活力有时间,但是没钱"  }
when (18 ..^ 70).contains($age) { say "你有活力有钱,但是没时间"  }
default                         { say "你有时间和金钱,但是没活力"}

匹配元组中的范围 #

my $user = ("twostraws", "fr0st1es", 36);

given $user {
    my $name = $user[0];
    when ($name, *, 0 ..^ 18)  { say "$name 有活力有时间,但是没钱"  }
    when ($name, *, 18 ..^ 70) { say "$name 有活力有钱,但是没时间"  }
    when ($name, *, *)         { say "$name 有时间和金钱,但是没活力" }
}

枚举 #

enum WeatherType <Cloudy Sunny Windy>;
my $today = WeatherType::Cloudy;

given $today {
    when WeatherType::Cloudy { say "多云" }
    when WeatherType::Sunny  { say "晴天" }
    when WeatherType::Windy  { say "有风" }
}

# 使用 if 语句
if $today ~~ WeatherType::Cloudy { say "多云" }

关联值 #

enum WeatherType  (
    Cloudy => 100,
    Sunny  => 50,
    Windy  => 30
);

my $today = WeatherType::Windy;
given $today {
    when WeatherType::Cloudy { say 20*Cloudy }
    when WeatherType::Sunny  { say 10*Sunny  }
    when WeatherType::Windy  { say 12*Windy  }
}

when 从句 #

my @numbers = 1..10;
.say when $_ % 2 == 1 for @numbers;

my @celebrities = "Michael Jackson", "Taylor Swift", "MichaelCaine", "Adele Adkins", "Michael Jordan";
.say when /^Michael/ for @celebrities;     # 使用正则表达式
.say when $_.chars > 12 for @celebrities;  # 调用方法
.say when /^Michael/ and $_.chars >12 for @celebrities; # 复合条件

proceed #

given-when 有两个小的改变, 并且这俩改变都是开启新行为的, 而不是限制已存在的行为。

第一个小的改变: when 的开关行为不仅仅是用于 given 块儿中的, 而是可以用在任何"主题化"的块儿中的, 例如 for 循环中或接收 $_ 作为参数的子例程中。

given $answer {
    when "Atlantis" { say "那是对的" }
    default { say "BZZZZZZZZZZZZZ!" }
}

for 1..100 {
    when * %% 15 { say "Fizzbuzz" }
    when * %% 3  { say "Fizz"     }
    when * %% 5  { say "Buzz"     }
    default      { say $_         }
}

sub expand($_) {
    when "R" { say "红警" }
    when "G" { say "绿警" }
    when "B" { say "蓝警" }
    default  { say $_     }
}

但是甚至不接受 $_ 作为参数的子例程也得到了它们自己的词法变量 $_ 供修改。所以规则就是"现在, 在 $_ 中有某些我能启动的好东西吗"。如果我们想要, 我们甚至能自己设置 $_

sub seek-the-answer() {
    $_ = (^100).pick;
    when 42 { say "The answer" }
    default { say "A number"   }
}

换句话说, 我们已经知道了 whengiven 是单独的。Switch 语句逻辑都在 when 语句中。

第二个小改变: 你可以嵌套 when 语句!

我很确信你没有在野外见过这种用法。但它有时候特别有用:

when * > 2 {
    when 4  { say 'Four!' }
    default { say 'huge'  }
}

default {
    say 'little'
}

你可能记得, 在 when 块儿中有一个隐式的 succeed 语句在末尾, 这让周围的主题化块退出。(意思是你不必记着手动退出 switch 语句)。如果你想重写 succeed 语句并继续通过 when block, 那么你在 when block 的末尾写上一个显式的 proceed 即可。

given $verse-number {
    when * >= 12 { say "Twelve drummers drumming"; proceed }
    when * >= 11 { say "Eleven pipers piping"; proceed     }
    # ...
    when * >= 5 { say "FIIIIIIVE GOLDEN RINGS!"; proceed }
    when * >= 4 { say "Four calling birds"; proceed      }
    when * >= 3 { say "Three French hens"; proceed       }
    when * >= 2 {
        say "Two turtle doves";
        say "and a partridge in a pear tree";
    }

    say "a partridge in a pear tree";
}