Perl 6中的模式匹配是否具有保护条款?

时间:2018-04-13 09:56:54

标签: scala switch-statement perl6 guard-clause

在scala中,pattern matchguard 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

4 个答案:

答案 0 :(得分:8)

我写了两个答案。这个回答标题中的问题。 (另一个回答你的帖子正文中的“这是对的吗?”问题。)

“Perl 6中的模式匹配是否具有保护条款?”

根据我对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书籍。希望如此。)

Scala的(|)和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;你的帖子正文中的问题。 (另一个回答标题中的问题。)

&#34;这是对的吗?&#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'

但是第三行会发生什么?好吧,Inttype 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 移出,外部 succeedgiven 移出 when 块。
如果内部 when 不匹配,我们希望继续外部 default 检查和 proceed,因此我们调用 when

这意味着我们还可以将多个 Int 语句组合到 when 案例中,从而无需重复进行类型检查。这也意味着如果我们不测试 Int 值,那些内部 when Int:D { when $_ < 10 { say 5 } when 10 { say 10} when $_ > 10 { say 65} } 检查根本不会发生。

{{1}}