第五天 - 使用 Raku 签名解构参数
在许多其他关键的 Raku 特性中,我认为 Signatures 是众多"杀手级"特性之一。 它们的功能如此丰富而强大,我怀疑关于如何使用它们可以写一整本书。 我想探索一下我原来忽略但是非常珍惜的一些特定功能。
您可能已经看到了基本的子程序签名:
sub myfunc($x, $y, $z) {...}
它给函数声明了 3 个标量参数, 并在函数体里面给了它们 $x, $y, $z 的名字。
太简单了。
你可以更有爱心, 给它们加上指定的类型:
sub myfunc(Str $x, Int $y, Rat $z) {...}
你可以使用笑脸符号 :D
让参数值是有定义的:
sub myfunc(Str:D $x, Int:D $y, Rat:D $z) {...}
还有很多其它花哨的说明符你可以使用,在这里我不深入了。
但是如果你的参数更复杂呢? (不是 Complex - 虽然它也起作用..)
For example, you might want to restrict a specific parameter to a Positional argument like an Array, or an Associative one like a Hash using the respective sigils, @ or %. 例如,你可能想要将特定的参数限制为像 Array 这样的 Positional 参数,或者使用相应的 @ 或 % 符号将这个参数限制为像 Hash 这样的关联参数。
sub myfunc(%h) {...}
现在我可以使用一个散列来调用该函数:
myfunc(%( a => 1, b => 'this', c => 2.2));
如果我想验证那些特定的字段是否存在,我可以把代码放在函数的顶部来做到这一点:
sub myfunc(%h) {
die "a must be an Int" unless %h<a> ~~ Int;
die "b must be a Str" unless %h<b> ~~ Str;
die "c must be a Rat" unless %h<c> ~~ Rat;
}
如果我还想简化引用那些字段的方式,我可以将它们赋值给其他变量:
sub myfunc(%h) {
die "a must be an Int" unless %h<a> ~~ Int;
die "b must be a Str" unless %h<b> ~~ Str;
die "c must be a Rat" unless %h<c> ~~ Rat;
my $a = %h<a>;
my $b = %h<b>;
my $c = %h<c>;
}
有点无聊,对吗?
Perl 签名参数解构来拯救你了! 我们可以在子例程签名自身中做所有的事情 - 只要放一个子签名(sub-signature)在后面即可。
sub myfunc(%h (Int :$a, Str :$b, Rat :$c)) {...}
解构 JSON
相当不错,但如果你有更复杂的东西呢?
假如说一块儿有嵌套结构的 JSON,某些部分可能缺失了, 它们需要默认值, 等等。
use JSON::Fast;
my $item = from-json(q:to/END/);
{
"book" : {
"title" : "A Christmas Carol",
"author" : "Charles Dickens"
},
"count" : 12,
"tags" : [ "christmas", "santa"]
}
END
q:to/END/
是一个 Raku heredoc,它直接在文本中直到 END,然后我们可以使用 JSON::Fast 的 from-json()
将其解析为 perl 中的数据结构。 你可以在函数签名中描述整个 JSON 结构,以便接收以下内容:
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
的尖号块中使用解构签名即可:
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
和 主题化的迭代器,但是使用子签名,你可以做更多。
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[]";
}
如果您想检查类型或定义,或设置默认值,您都可以在签名中正确地执行。 如果您不喜欢对象属性的名称,则可以使用别名来重命名它们, 你开心就行。
结论
我发现解构参数在与数据库查询结果和 JSON 交互中非常有用。 您可以使用任何其他签名特性,包括指定类型,定义,可选性,默认值,使用别名重命名,使用子集约束或“where”从句,slurpies等。
节日快乐!