在scala中,pattern match
有guard pattern
:
val ch = 23
val sign = ch match {
case _: Int if 10 < ch => 65
case '+' => 1
case '-' => -1
case _ => 0
}
Perl 6版本是这样的吗?
my $ch = 23;
given $ch {
when Int and * > 10 { say 65}
when '+' { say 1 }
when '-' { say -1 }
default { say 0 }
}
这是对的吗?
更新:正如 jjmerelo 建议的那样,我发布的结果如下,签名版本也很有趣。
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
答案 0 :(得分:8)
我写了两个答案。这个回答标题中的问题。 (另一个回答你的帖子正文中的“这是对的吗?”问题。)
根据我对Scala的了解,一些/大多数Scala模式匹配实际上对应于使用Perl 6签名。 (在这种情况下,保护条款通常为where
clauses。)
引用Scala的创建者Martin Odersky来自The Point of Pattern Matching in Scala:
而不仅仅是匹配数字,这就是switch语句所做的,你匹配的本质上就是对象的创建形式
Perl 6 signatures涵盖了几个用例(yay,puns)。这些包括功能编程范式使用的Perl 6等价物,其中一个匹配值'或函数'类型签名(参见Haskell)和面向对象编程范例用法,其中一个匹配嵌套数据/对象并拉出所需位(cf阶)。
考虑这个Perl 6代码:
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
,如果他们的妈妈的名字与特定的正则表达式匹配,可能会拉出人的腿的“名字”(“左”,“中间”等)或者其他什么 - 你希望得到这张照片。
无需解构此人的默认情况(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添加了一个参数。鉴于我们对这种混乱的结构/数据没有任何作用,我们可以只编写{{1} }。)
不幸的是,我认为官方文档中没有提到签名解构。有人可以写一本关于Perl 6签名的书。 (从字面上看。当然,这是一种很好的方式 - 唯一的方式,甚至 - 写东西。我最喜欢的文章解析了Perl 6签名的一些力量,从2013年起由Moritz Pattern Matching and Unpacking。 em>已经撰写了Perl 6书籍。希望如此。)
(|)
和Perl 6的match/case
似乎更简单确实
正如@jjmerelo在评论中指出的那样,使用签名意味着每个案例都有一个given/when
,这在句法上比multi foo (...) { ...}
重得多。
缓解:
更简单的案例可以使用case ... => ...
,就像您在问题正文中所写的一样;
Perl 6可能有一天会得到非实验性的宏,可以用来实现一个看起来更接近Scala的given/when
/ match
结构的构造,从而避免重复{{1 }}第
答案 1 :(得分:6)
从我在this answer中看到的情况来看,这并不是Haskell具有相同意义上的guard pattern的实现。但是,Perl 6确实有与Scala相同的保护:使用与ifs结合的默认模式。
Haskell to Perl 6 guide确实有section on guards。它暗示使用where
作为警卫;所以这可能会回答你的问题。
答案 2 :(得分:3)
我写了两个答案。这个回答了“这是对的吗?&#34;你的帖子正文中的问题。 (另一个回答标题中的问题。)
没有
given $ch {
when Int and * > 10 { say 65}
}
此代码显示65
任何给定的整数,而不只是10
上的一个!
我们可以开始看到此代码的潜在问题:
.say for ('TrueA' and 'TrueB'),
('TrueB' and 'TrueA'),
(Int and 42),
(42 and Int)
显示:
TrueB
TrueA
(Int)
(Int)
and
构造boolean计算其左手参数。如果计算结果为False
,则返回它,否则返回右手参数。
在第一行中,'TrueA'
布尔值计算为True
,因此第一行返回右手参数'TrueB'
。
在第二行'TrueB'
评估为True
,以便and
返回其右手参数,在本例中为'TrueA'
。
但是第三行会发生什么?好吧,Int
是type object。键入对象boolean evaluate to False
!因此,and
适当地返回其左手参数Int
(.say
然后显示为(Int)
)。
这是问题的根源。
(为了继续苦头,编译器评估表达式Int and * > 10
;立即将左侧参数返回and
Int
;然后成功匹配Int
对任何整数given
- 完全忽略看起来像保护子句的代码(and ...
位)。)
如果你使用这样的表达式作为if
语句的条件,那么Int
将布尔值评估为False
并且你得到假阴性。在这里,您使用的when
使用.ACCEPTS会导致误报(它是一个整数,但它是任何整数,无视所谓的保护条款)。这个问题非常合理地属于traps page。
Brad ++对您的问题的评论显示了一个很好的解决方案:
when $_ ~~ Int and $_ > 10 { say 65 }
另外几种可能性:
when * ~~ Int and * > 10 { say 65 }
when my Int $ where $_ > 10 { say 65 }
when Int { proceed unless $_ > 10; { say 65 } }
另见我的其他答案。
答案 3 :(得分:2)
多年前,我写了一条评论,提到您必须更明确地像这样匹配 $_
:
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
语句组合到 when
案例中,从而无需重复进行类型检查。这也意味着如果我们不测试 Int
值,那些内部 when Int:D {
when $_ < 10 { say 5 }
when 10 { say 10}
when $_ > 10 { say 65}
}
检查根本不会发生。
{{1}}