Skip to content

Latest commit

 

History

History
303 lines (236 loc) · 8.53 KB

第二十章-解构.adoc

File metadata and controls

303 lines (236 loc) · 8.53 KB

解构

解构和赋值

解构和赋值可以结合使用。

deconstruction and assignment
my (Str $a, Str $b, Int $c) = <a b>;
say [$a, $b, $c].perl;
# OUTPUT«["a", "b", Int]␤»

要跳过列表中的元素, 使用匿名状态变量 $

skip variable with $
my ($,$a,$,%h) = ('a', 'b', [1,2,3], {:1th});
say [$a, %h].perl;
# OUTPUT«["b", {:th(1)}]␤»

使用签名解构参数

pass hash to routine
sub myfunc(%h) {...}

现在我可以使用一个散列来调用该函数:

call myfunc with hash
my %h = a => 1, b => 'this', c => 2.2;
myfunc(%h);

如果我想验证某些特定的键是否符合某种类型:

parameters constraint with where clause
my %h = a => 1, b => 'this', c => 2.2;

sub myfunc(
    %h where
    .<a> ~~ Int &&
    .<b> ~~ Str &&
    .<c> ~~ Rat
) {
    say %h.perl;
}

myfunc(%h);

如果我还想把散列的各个键赋值给其他变量:

parameters constraint with where clause
sub myfunc(
    %h where
    .<a> ~~ Int &&
    .<b> ~~ Str &&
    .<c> ~~ Rat
) {
    my $a = %h<a>;
    my %b = %h<b>;
    my %c = %h<c>;
}

这样很烦, 因为把散列的值赋值给变量, 这样的语句可能要重复很多次, 而且你也发现了这会重复引用那个散列, 很多次!

参数后面可以跟着用圆括号包围的子签名, 这就会解构所给定的参数.

hash deconstruction
sub myfunc(%h (Int :$a, Str :$b, Rat :$c)) {...}

列表解构

列表的解构正好是列表的元素:

first element of array using destructuring
sub first( @array ($first, *@rest) ) { $first }
sub first(     *@ [$first, *@]     ) { $first }
sub first(        [$first, *@]     ) { $first }

很神奇吧, 你不用在函数体里面写出 first 函数的逻辑, 你只使用子签名, 就自动获得了获取列表首元素的逻辑。

tail elements of array using destructuring
sub tail( @array ($first, *@tail) ) { @tail }
sub tail(     *@ [$,      *@tail] ) { @tail }
sub tail(        [$,      *@tail] ) { @tail }

外部参数是可以是匿名的 (@)。匿名参数可以省略。

散列解构

散列的解构是它的 pairs:

multi key-type ($ (Numeric :$key, *%)) { "Number" }
multi key-type ($ (Str     :$key, *%)) { "String" }
for (42 => 'a', 'b' => 42) -> $pair {
    say key-type $pair;
}

输出:

Number
String

因为 构造了一个 Pair,它有一个 key 和一个 value 属性。子签名中的 :$key 命名参数提取属性 key% 中的 % 是匿名的, % 接收所有剩余参数到一个匿名散列中, 不予使用。

anonymous hash deconstruction
sub all-dimensions(% (:length(:$x), :width(:$y), :depth(:$z))) {
    $x andthen $y andthen $z andthen True
}

尖号块儿循环也能解构散列:

hash deconstruction with pointy block
my %hhgttu = (:40life, :41universe, :42everything);
for %hhgttu -> (:$key, :$value) {
  say "$key$value";
}
# OUTPUT: «universe → 41␤life → 40␤everything → 42␤»

通常, 对象是基于对象的属性来解构的. 惯用法就是在 for 循环中解包一个 Pair 的键和值:

pairs deconstruction with pointy block
for <Peter Paul Merry>.pairs -> (:key($index), :value($guest)) { }
hash deconstruction with pointy block
subset Seconds of Numeric;

my regex number { \d+ [ '.' \d+ ]? } # float
my regex suffix { <:alpha> } # 只匹配字母

# 每天, 每小时, 每分钟, 每秒所对应的秒数
my %unit-multipliers = 'd' => 60*60*24, 'h' => 60*60, 'm' => 60, 's' => 1;

sub MAIN(*@timicles where .all ~~ /<number> <[dhms]>/) {
    my Seconds $to-wait = @timicles»\
        .match(/<number> <suffix>+/)».hash\
        .map(-> %( Rat(Any) :$number, Str(Any) :$suffix) { %unit-multipliers{$suffix} * $number } )\
        .sum;
    say $to-wait ~ "s";
}
timicles.pl6
perl6 timicles.pl6 1d 2h 3m 5s

对象解包为它们的属性只是默认行为. 要使对象以另外的方式解构, 要更改它的 Capture.

解构 JSON

相当不错,但如果你有复杂的东西呢?

假如说一块儿有嵌套结构的 JSON,某些部分可能缺失了, 它们需要默认值, 等等。

parse json to hash object
use JSON::Fast;
my $item = from-json(q:to/END/);
    {
        "book" : {
            "title"  : "A Christmas Carol",
            "author" : "Charles Dickens"
        },
        "count" : 12,
        "tags" : [ "christmas", "santa"]
    }
    END

我们可以使用 JSON::Fastfrom-json() 将其解析为 perl 中的数据结构。 你可以在函数签名中描述整个 JSON 结构,以便接收以下内容:

anonymous hash deconstruction
sub myfunc(% (:%book (Str:D :$title, Str:D :$author), Int :$count,
              :@tags ($first-tag, *@other-tags)) )
{...}

现在,在函数体中,我可以将这些部分引用为 $title$author$count`和 `@tags。 为了方便起见,我还将标签分成了 $first-tag@other-tags

在块儿中使用签名

当然,签名对于子程序来说是幻想的,但是你也可以在块儿(Block)中使用签名和解构。 假设你有一个上面的 JSON 条目的数组,并希望通过一个 for 循环遍历它们? 只需在 for 的尖号块中使用解构签名即可:

hash deconstruction with pointy block
for @itemlist -> % (:%book (Str:D :$title, Str:D :$author), Int :$count,
                    :@tags ($first-tag, *@other-tags))
{
    say "$title, $author, $count, @tags[], $first-tag, @other-tags[]"
}

注意在这种情况下,我甚至不需要散列本身,所以我省略了散列的名称,仅使用 作为匿名散列(关联)。

你甚至可以解构对象!

你有没有试过遍历一组对象,你所做的第一件事是调用一些访问器来获取一些属性? 当然,你可以使用 .attribute 和 主题化的迭代器,但是使用子签名,你可以做更多。

object deconstruction
class Book {
    has $.title;
    has $.author;
    has $.count;
    has @.tags;
}

my @booklist =
    Book.new(title => 'A Christmas Carol',
             author => 'Charles Dickens',
             count => 12,
             tags => <ghost christmas>),

    Book.new(title => 'A Visit from St. Nicholas',
             author => 'Clement Clarke Moore',
             count => 4,
             tags => <santa christmas>);

for @booklist -> Book $b (:$title,:$author, :$count, :@tags) {
    say "$title, $author, $count, @tags[]";
}

再看一个例子:

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"

如果您想检查类型或定义,或设置默认值,您都可以在签名中正确地执行。 如果您不喜欢对象属性的名称,则可以使用别名来重命名它们, 你开心就行。

结论

我发现解构参数在与数据库查询结果和 JSON 交互中非常有用。 您可以使用任何其他签名特性,包括指定类型,定义,可选性,默认值,使用别名重命名,使用子集约束或“where”从句,slurpies等。