匿名吞噬者

Anonymous slurpers

Anonymous slurpers

我有一个脚本,我只对其输出的最后两行感兴趣。用 Shell::Piping 就可以轻松处理。

px«script.sh ./foo.txt» |» my @a;
my ($second-last-line, $last-line) = @a.tail(2);

这样做是可行的,但很浪费,因为它将文本行存储在 @a 中,并将它们保留在周围,直到 Array 退出作用域。另外,任何临时变量都是一个明显的样板指标。而且我们在 raku.land 中不使用样板。

声明符是相当强大的,因为它们可以接受一个列表并立即将其交给 infix:<=>。我们甚至可以通过使用匿名标量来跳过值。

my ($, $a, $b) = sub-that-returns-a-list();

我想做一些很类似的事情。寻找最后两个元素的意思是跳过除了最后两个元素以外的任何其它元素。在下标和签名中,我们用 Whatever * 来表示多重性。(有时候我搞不懂英文,为什么 “manyness” 不是一个词?o.O)

px«script.sh ./foo.txt» |» my (*, $second-last, $last);

这不太行得通,因为 Rakudo 并不期待声明符列表中的那个什么星。事实上,它并不期望在那个地方有任何项。我们可以通过显式来解决这个问题。当我们在做这件事的时候,我们可以在其中添加一个匿名标量。

px«script.sh ./foo.txt» |» my (Whatever, $third-last, $, $last);

声明符返回一个容器和值的 List。我们可以使用自省来剖析它。

my \l := my (Whatever, $second-last, $, $last);
say l».&{ .WHAT, .VAR.WHAT, .VAR.name };
# OUTPUT: (((Whatever) Whatever anon) ((Any) Any $second-last) ((Any) Any anon) ((Any) Any $last))

通过检查类型对象和 .VAR 中的类型对象和名称,我们可以区分 *$ 和普通容器。

sub infix:<|»>(\l, \r) {
    sub is-whatever($_ is raw) { ( .VAR.name eq 'anon' && .WHAT === Whatever ) but role :: { method gist { self ?? '*' !! ''    } } }
    sub is-anon($_ is raw)     { ( .VAR.name eq 'anon' && .WHAT === Any )      but role :: { method gist { self ?? 'anon' !! '' } } }
    sub is-scalar($_ is raw)   { ( .VAR ~~ Scalar && .WHAT !=== Whatever && .VAR.name ne 'anon' ) but role :: { method gist { self ?? 'Scalar' !! '' } } }

    sub is-left-slurpy(\l)  { l.head.&is-whatever but role :: { method gist { self ?? 'left-slurpy: yes'  !! 'left-slurpy: no' } } }
    sub is-right-slurpy(\l) { l.tail.&is-whatever but role :: { method gist { self ?? 'right-slurpy: yes' !! 'left-slurpy: no' } } }

    say r».&{ .&is-whatever, .&is-anon, .&is-scalar }
    say r.&is-left-slurpy;
    say r.&is-right-slurpy;
}

42 |» my (W, $, $c);
42 |» my ($d, $e, W);

# OUTPUT: ((*  ) ( anon ) (  Scalar))
          left-slurpy: yes
          left-slurpy: no
          ((  Scalar) (  Scalar) (*  ))
          left-slurpy: no
          right-slurpy: yes

现在我有了教 Shell::Piping 跳过值的所有东西。

Rakudo 不允许在声明符列表中使用 Whatever 星号,这感觉是个 bug。

my @List = my (42, "", Cool, $, $a);
dd @List;
Array @List = [Mu, Mu, Cool, Any, Any]

它完全可以跳过字面值,取用类型对象并保留容器,但不管什么原因,它不喜欢星号。然而,确实有效的东西,真的很好用。它确实为我们提供了容器,我们可以通过 .VAR 进行推理。看来,圣诞老人是有发现的。

comments powered by Disqus