Raku 中的模式匹配是否有保护子句?
Does pattern match in Raku have guard clause?
在 scala 中,pattern match
有 guard pattern
:
val ch = 23
val sign = ch match {
case _: Int if 10 < ch => 65
case '+' => 1
case '-' => -1
case _ => 0
}
Raku版是这样的吗?
my $ch = 23;
given $ch {
when Int and * > 10 { say 65}
when '+' { say 1 }
when '-' { say -1 }
default { say 0 }
}
这样对吗?
更新: 按照jjmerelo的建议,我post我的结果如下,签名版也有意思。
multi washing_machine(Int \x where * > 10 ) { 65 }
multi washing_machine(Str \x where '+' ) { 1 }
multi washing_machine(Str \x where '-' ) { -1 }
multi washing_machine(\x) { 0 }
say washing_machine(12); # 65
say washing_machine(-12); # 0
say washing_machine('+'); # 1
say washing_machine('-'); # -1
say washing_machine('12'); # 0
say washing_machine('洗衣机'); # 0
从我在this answer, that's not really an implementation of a guard pattern中所见,同理Haskell也有。然而,Perl 6 确实具有与 Scala 具有相同意义的守卫:使用默认模式与 ifs 相结合。
Haskell to Perl 6 guide does have a section on guards。它暗示使用 where
作为守卫;这样可能会回答你的问题。
TL;DR 我写了另一个着重于使用 when
的答案。这个答案的重点是使用一种替代方法,将 Raku 强大的模式匹配结构 Signature
s 与 where
子句结合起来。
"Raku 中的模式匹配是否有保护子句?"
基于我对Scala的了解,some/most Scala模式匹配实际上对应于使用Raku签名。 (在这种情况下,保护条款通常是 where
clauses。)
引用 Scala 的创建者 Martin Odersky,来自 The Point of Pattern Matching in Scala:
instead of just matching numbers, which is what switch statements do, you match what are essentially the creation forms of objects
Raku signatures 涵盖多个用例(是的,双关语)。这些包括函数式编程范式使用的 Raku 等价物,其中匹配值或函数的类型签名(cf Haskell)和面向对象编程范式使用,其中匹配嵌套 data/objects 并拉取输出所需的位(cf Scala)。
考虑这个 Raku 代码:
class body { has ( $.head, @.arms, @.legs ) } # Declare a class (object structure).
class person { has ( $.mom, $.body, $.age ) } # And another that includes first.
multi person's-age-and-legs # Declare a function that matches ...
( person # ... a person ...
( :$age where * > 40, # ... whose age is over 40 ...
:$body ( :@legs, *% ), # ... noting their body's legs ...
*% ) ) # ... and ignoring other attributes.
{ say "$age {+@legs}" } # Display age and number of legs.
my $age = 42; # Let's demo handy :$var syntax below.
person's-age-and-legs # Call function declared above ...
person # ... passing a person.
.new: # Explicitly construct ...
:$age, # ... a middle aged ...
body => body.new:
:head,
:2arms,
legs => <left middle right> # ... three legged person.
# Displays "42 3"
注意上面有一个与 Scala 模式匹配保护子句非常接近的等效项 -- where * > 40
。 (这可以很好地捆绑成 subset
type。)
我们可以定义对应于不同情况的其他 multi
s,也许可以提取人腿的“名称”('left'、'middle' 等)妈妈的名字与特定的正则表达式或其他任何内容匹配 - 希望您能理解。
一个默认情况(multi
)不费心去解构这个人可能是:
multi person's-age-and-legs (|otherwise)
{ say "let's not deconstruct this person" }
(在上文中,我们在签名中为参数添加了 |
到 slurp up all remaining structure/arguments passed to a multi 的前缀。假设我们不对 structure/data 执行任何操作,我们可以只写(|)
.)
不幸的是,我认为官方文档中没有提到签名解构。有人可以写一本关于 Raku 签名的书。 (从字面上看。这当然是一种很好的方式 - 甚至是唯一的方式 - 来写东西。我最喜欢的文章是 Pattern Matching and Unpacking 从 2013 年起,作者 Moritz。谁 有 人撰写了 Raku 书籍。希望如此。)
Scala 的 match/case
和 Raku 的 given/when
似乎更简单
确实如此。
正如@jjmerelo 在评论中指出的那样,使用签名意味着每个案例都有一个 multi foo (...) { ...}
,这在语法上比 case ... => ...
.
重得多
缓解中:
比较简单的情况可以用given/when
,就像你在问题正文中写的那样;
Raku 大概有一天会得到非实验性的宏,可用于实现一个看起来更接近 Scala 的 match
/case
构造的构造,省略重复的multi foo (...)
s.
TL;DR 您是否遇到过我所说的 WTF?!?:when Type and ...
无法检查 and
子句。这个答案讨论了 when
有什么问题以及如何解决它。我写了另一个答案,重点是使用 where
和签名。
如果你想坚持使用 when
,我建议这样做:
when (condition when Type) { ... } # General form
when (* > 10 when Int) { ... } # For your specific example
这(imo)并不令人满意,但它确实首先检查 Type
作为守卫,然后检查守卫是否通过的条件,并按预期工作。
“这样对吗?”
没有
given $ch {
when Int and * > 10 { say 65}
}
此代码表示 65
对于 任何 给定整数,而不仅仅是 10
!
卧槽?!? Imo 我们应该在 Raku's trap page.
上提到这个
如果 when
构造以类型对象的编译时常量值开始,并以 and
继续(或 &&
、andthen
等),其中 .它可能会在编译时失败或显示警告。
这是我能想到的最佳选择:
when (* > 10 when Int) { say 65 }
这利用了括号内 when
的语句修饰符(又名后缀)形式。 Int
在 * > 10
.
之前 检查
这受到 Brad++ 的新答案的启发,如果您针对单个保护条款编写多个条件,该答案看起来不错。
我认为我的变体比我在此答案的先前版本中提出的其他选项更好,但仍然不能令人满意,因为我不喜欢即将到来的 Int
在条件之后。
最终,尤其是 if/when RakuAST 登陆,我认为我们将尝试新的模式匹配形式。希望我们能想出一些好的方法来很好地消除这种疣。
真的吗?怎么回事?
我们可以开始看到这段代码的潜在问题:
.say for ('TrueA' and 'TrueB'),
('TrueB' and 'TrueA'),
(Int and 42),
(42 and Int)
显示:
TrueB
TrueA
(Int)
(Int)
and
构造布尔值计算其左侧参数。如果计算结果为 False
,则它 returns,否则它 returns 它的右手参数。
在第一行中,'TrueA'
布尔值的计算结果为 True
,因此第一行 returns 右侧参数 'TrueB'
.
在第二行中,'TrueB'
的计算结果为 True
,因此 and
returns 它的右手参数,在本例中为 'TrueA'
.
但是第三行发生了什么?好吧,Int
是一个 type object。类型对象布尔值评估为 False
!因此 and
适当地 returns 它的左手参数是 Int
(.say
然后显示为 (Int)
)。
这就是问题的根源。
(为了继续苦涩的结局,编译器计算表达式 Int and * > 10
;立即 returns and
的左侧参数,即 Int
;然后成功地将 Int
与任何整数 given
匹配——完全忽略看起来像保护子句的代码(and ...
位)。)
如果您使用这样的表达式作为 if
语句的条件,则 Int
的布尔值计算结果为 False
并且您会得到假阴性.在这里,您使用的是 when
,它使用 .ACCEPTS which leads to a false positive (it is an integer but it's any integer, disregarding the supposed guard clause). This problem quite plausibly belongs on the traps page.
几年前我写了一条评论提到你必须更明确地匹配 $_
像这样:
my $ch = 23;
given $ch {
when $_ ~~ Int and $_ > 10 { say 65}
when '+' { say 1 }
when '-' { say -1 }
default { say 0 }
}
回到这个问题后,我意识到还有另一种方法。
when
可以安全地位于另一个 when
结构中。
my $ch = 23;
given $ch {
when Int:D {
when $_ > 10 { say 65}
proceed
}
when '+' { say 1 }
when '-' { say -1 }
default { say 0 }
}
请注意,内部 when
将 succeed
超出外部,后者将 succeed
超出 given
块。
如果内部 when
不匹配,我们要继续进行外部 when
检查和 default
,所以我们调用 proceed
.
这意味着我们还可以将多个 when
语句分组到 Int
案例中,从而避免重复进行类型检查。这也意味着如果我们不测试 Int
值,那些内部 when
检查根本不会发生。
when Int:D {
when $_ < 10 { say 5 }
when 10 { say 10}
when $_ > 10 { say 65}
}
在 scala 中,pattern match
有 guard pattern
:
val ch = 23
val sign = ch match {
case _: Int if 10 < ch => 65
case '+' => 1
case '-' => -1
case _ => 0
}
Raku版是这样的吗?
my $ch = 23;
given $ch {
when Int and * > 10 { say 65}
when '+' { say 1 }
when '-' { say -1 }
default { say 0 }
}
这样对吗?
更新: 按照jjmerelo的建议,我post我的结果如下,签名版也有意思。
multi washing_machine(Int \x where * > 10 ) { 65 }
multi washing_machine(Str \x where '+' ) { 1 }
multi washing_machine(Str \x where '-' ) { -1 }
multi washing_machine(\x) { 0 }
say washing_machine(12); # 65
say washing_machine(-12); # 0
say washing_machine('+'); # 1
say washing_machine('-'); # -1
say washing_machine('12'); # 0
say washing_machine('洗衣机'); # 0
从我在this answer, that's not really an implementation of a guard pattern中所见,同理Haskell也有。然而,Perl 6 确实具有与 Scala 具有相同意义的守卫:使用默认模式与 ifs 相结合。
Haskell to Perl 6 guide does have a section on guards。它暗示使用 where
作为守卫;这样可能会回答你的问题。
TL;DR 我写了另一个着重于使用 when
的答案。这个答案的重点是使用一种替代方法,将 Raku 强大的模式匹配结构 Signature
s 与 where
子句结合起来。
"Raku 中的模式匹配是否有保护子句?"
基于我对Scala的了解,some/most Scala模式匹配实际上对应于使用Raku签名。 (在这种情况下,保护条款通常是 where
clauses。)
引用 Scala 的创建者 Martin Odersky,来自 The Point of Pattern Matching in Scala:
instead of just matching numbers, which is what switch statements do, you match what are essentially the creation forms of objects
Raku signatures 涵盖多个用例(是的,双关语)。这些包括函数式编程范式使用的 Raku 等价物,其中匹配值或函数的类型签名(cf Haskell)和面向对象编程范式使用,其中匹配嵌套 data/objects 并拉取输出所需的位(cf Scala)。
考虑这个 Raku 代码:
class body { has ( $.head, @.arms, @.legs ) } # Declare a class (object structure).
class person { has ( $.mom, $.body, $.age ) } # And another that includes first.
multi person's-age-and-legs # Declare a function that matches ...
( person # ... a person ...
( :$age where * > 40, # ... whose age is over 40 ...
:$body ( :@legs, *% ), # ... noting their body's legs ...
*% ) ) # ... and ignoring other attributes.
{ say "$age {+@legs}" } # Display age and number of legs.
my $age = 42; # Let's demo handy :$var syntax below.
person's-age-and-legs # Call function declared above ...
person # ... passing a person.
.new: # Explicitly construct ...
:$age, # ... a middle aged ...
body => body.new:
:head,
:2arms,
legs => <left middle right> # ... three legged person.
# Displays "42 3"
注意上面有一个与 Scala 模式匹配保护子句非常接近的等效项 -- where * > 40
。 (这可以很好地捆绑成 subset
type。)
我们可以定义对应于不同情况的其他 multi
s,也许可以提取人腿的“名称”('left'、'middle' 等)妈妈的名字与特定的正则表达式或其他任何内容匹配 - 希望您能理解。
一个默认情况(multi
)不费心去解构这个人可能是:
multi person's-age-and-legs (|otherwise)
{ say "let's not deconstruct this person" }
(在上文中,我们在签名中为参数添加了 |
到 slurp up all remaining structure/arguments passed to a multi 的前缀。假设我们不对 structure/data 执行任何操作,我们可以只写(|)
.)
不幸的是,我认为官方文档中没有提到签名解构。有人可以写一本关于 Raku 签名的书。 (从字面上看。这当然是一种很好的方式 - 甚至是唯一的方式 - 来写东西。我最喜欢的文章是 Pattern Matching and Unpacking 从 2013 年起,作者 Moritz。谁 有 人撰写了 Raku 书籍。希望如此。)
Scala 的 match/case
和 Raku 的 given/when
似乎更简单
确实如此。
正如@jjmerelo 在评论中指出的那样,使用签名意味着每个案例都有一个 multi foo (...) { ...}
,这在语法上比 case ... => ...
.
缓解中:
比较简单的情况可以用
given/when
,就像你在问题正文中写的那样;Raku 大概有一天会得到非实验性的宏,可用于实现一个看起来更接近 Scala 的
match
/case
构造的构造,省略重复的multi foo (...)
s.
TL;DR 您是否遇到过我所说的 WTF?!?:when Type and ...
无法检查 and
子句。这个答案讨论了 when
有什么问题以及如何解决它。我写了另一个答案,重点是使用 where
和签名。
如果你想坚持使用 when
,我建议这样做:
when (condition when Type) { ... } # General form
when (* > 10 when Int) { ... } # For your specific example
这(imo)并不令人满意,但它确实首先检查 Type
作为守卫,然后检查守卫是否通过的条件,并按预期工作。
“这样对吗?”
没有
given $ch {
when Int and * > 10 { say 65}
}
此代码表示 65
对于 任何 给定整数,而不仅仅是 10
!
卧槽?!? Imo 我们应该在 Raku's trap page.
上提到这个如果 when
构造以类型对象的编译时常量值开始,并以 and
继续(或 &&
、andthen
等),其中 .它可能会在编译时失败或显示警告。
这是我能想到的最佳选择:
when (* > 10 when Int) { say 65 }
这利用了括号内 when
的语句修饰符(又名后缀)形式。 Int
在 * > 10
.
这受到 Brad++ 的新答案的启发,如果您针对单个保护条款编写多个条件,该答案看起来不错。
我认为我的变体比我在此答案的先前版本中提出的其他选项更好,但仍然不能令人满意,因为我不喜欢即将到来的 Int
在条件之后。
最终,尤其是 if/when RakuAST 登陆,我认为我们将尝试新的模式匹配形式。希望我们能想出一些好的方法来很好地消除这种疣。
真的吗?怎么回事?
我们可以开始看到这段代码的潜在问题:
.say for ('TrueA' and 'TrueB'),
('TrueB' and 'TrueA'),
(Int and 42),
(42 and Int)
显示:
TrueB
TrueA
(Int)
(Int)
and
构造布尔值计算其左侧参数。如果计算结果为 False
,则它 returns,否则它 returns 它的右手参数。
在第一行中,'TrueA'
布尔值的计算结果为 True
,因此第一行 returns 右侧参数 'TrueB'
.
在第二行中,'TrueB'
的计算结果为 True
,因此 and
returns 它的右手参数,在本例中为 'TrueA'
.
但是第三行发生了什么?好吧,Int
是一个 type object。类型对象布尔值评估为 False
!因此 and
适当地 returns 它的左手参数是 Int
(.say
然后显示为 (Int)
)。
这就是问题的根源。
(为了继续苦涩的结局,编译器计算表达式 Int and * > 10
;立即 returns and
的左侧参数,即 Int
;然后成功地将 Int
与任何整数 given
匹配——完全忽略看起来像保护子句的代码(and ...
位)。)
如果您使用这样的表达式作为 if
语句的条件,则 Int
的布尔值计算结果为 False
并且您会得到假阴性.在这里,您使用的是 when
,它使用 .ACCEPTS which leads to a false positive (it is an integer but it's any integer, disregarding the supposed guard clause). This problem quite plausibly belongs on the traps page.
几年前我写了一条评论提到你必须更明确地匹配 $_
像这样:
my $ch = 23;
given $ch {
when $_ ~~ Int and $_ > 10 { say 65}
when '+' { say 1 }
when '-' { say -1 }
default { say 0 }
}
回到这个问题后,我意识到还有另一种方法。
when
可以安全地位于另一个 when
结构中。
my $ch = 23;
given $ch {
when Int:D {
when $_ > 10 { say 65}
proceed
}
when '+' { say 1 }
when '-' { say -1 }
default { say 0 }
}
请注意,内部 when
将 succeed
超出外部,后者将 succeed
超出 given
块。
如果内部 when
不匹配,我们要继续进行外部 when
检查和 default
,所以我们调用 proceed
.
这意味着我们还可以将多个 when
语句分组到 Int
案例中,从而避免重复进行类型检查。这也意味着如果我们不测试 Int
值,那些内部 when
检查根本不会发生。
when Int:D {
when $_ < 10 { say 5 }
when 10 { say 10}
when $_ > 10 { say 65}
}