Perl 6 Weekly(一)

1、在 Perl 6 中怎样检查文件的时间戳属性?

在 Perl 6 中怎样检查文件的时间戳属性? 在 Perl 5 中是使用文件测试操作符 file test operators , 在 Perl 6 中是使用来自于 IO::FileTestable role 的方法 (e.g. .modified, .accessed and .changed) 。

例如:

my $filename = "sample.txt";
my $seconds_since_epoch = $filename.IO.accessed;
my $readable_timestamp  = DateTime.new($filename.IO.accessed);

say "File '$filename' was last accessed at '$readable_timestamp', which is {$seconds_since_epoch.Num} seconds since the epoch";

2、我正尝试生成包含 10 个随机随机序列的 FASTQ 文件, 序列由随机品质分数构成。我原来是使用下面的代码,它工作良好:

my @seq  = (rand_fa_seq() for ^10);
my @qual = (rand_qual()   for ^10);

@seq.perl.say;
@qual.perl.say;

sub rand_fa_seq
{
    return join("", roll(20,"ACGT".comb));

}

sub rand_qual
{
    return join("", roll(20,"EFGHIJ".comb))
}

等价于:

sub rand-fa-seq($n = 20) { .roll($n).join }
sub rand-qual($n = 20)   { .roll($n).join }

my @seq  = rand-fa-seq() xx 10;
my @qual = rand-qual() xx 10;

3、在 Perl 6 中使用 "列表解析” 生成非平方数列表

在 Perl 6 中我怎样使用 "列表解析" 创建一组非平方数? 我在 Rosetta Code 那儿看到了如何打印一组非平方数的代码:

sub nth_term (Int $n) { $n + round sqrt $n }

say nth_term $_ for 1 .. 22;

目前为止我看到的最接近的东西是使用 for 关键字。 但是因为这实际上仅仅是一个内联(inline)循环,我认为这从技术上来讲并不是列表解析,尽管它看起来相似:

my @y = ($_**2 + 1 for 1 .. 10);

但是,我真正想知道是否有一种 “列表解析 “ 的方法来创建可在数学上描述的诸如非平方数的列表。这儿有一个我用来创建一组非平方数的方法(直到 30):

my @non_squares = grep {sqrt($_) != floor(sqrt($_))}, 1 .. 30;

我怎样用列表解析来实现它呢?

实际上, 你的例子 my @y = ($_**2 + 1 for 1 .. 10); 是 Perl 6 方式写成的列表解析。你还可以添加一个条件测试, 就像 Perl 6 design document S04 中建议的那样:

为了轻松地书写列表解析, 循环语句修饰符允许包含单个条件语句修饰符:

sub odd(Int $n) {return $n % 2}
@evens = ($_ * 2 if .&odd for 0..100);

这个就是怎样写一个 Perl 6 列表解析的非平方数(直到 30):

my @non_squares = ($_ if .sqrt != .sqrt.Int for 1 .. 30);

一丢丢解释:在每次 for 循环迭代中, 从 1 到 30 这个范围中的当前数字会被赋值给默认变量 $_(等价于 it)。没有调用者的方法调用会默认在 “it" 身上调用(例如 .sqrt 等价于 $_.sqrt)。 所以,对于 1到30中的每一个数字,它的平方根被检查以查看它是否有非整数平方根。 如果是真, 那它就被包含在列表中。

4、Perl 6 中的 Print 函数和冒号

我想知道在 Perl 6 中冒号与方法和函数调用有什么关系。

我在 Perl6 spec test (S32-io) 中看到了这个(我添加了注释):

$fh.print: "0123456789A";   # prints '0123456789A' to the file

据我所知,这等价于:

$fh.print("0123456789A");   # prints '0123456789A' to the file

这两种方式看起来都接收多个参数而且展平列表也没问题:

$fh.print: "012", "345", "6789A";   # prints '0123456789A' to the file
$fh.print("012", "345", "6789A");   # prints '0123456789A' to the file

my @a = <012 345 6789A>;

$fh.print(@a);   # prints '0123456789A' to the file
$fh.print: @a;   # prints '0123456789A' to the file

存在这两种语法一定有某种原因。 使用这种或另一种语法有某种理由吗?

我还注意到,当作为方法使用时, 我们不得不使用带有 :()的 print:

$fh.print(@a);   # Works
$fh.print: @a;   # Works!
$fh.print @a;    # ERROR!

当使用带冒号的 print 函数时,还有一些有意思的行为。 在这种情况下, : 和 () 不等价:

print @a;  # Prints '0123456789A' (no newline, just like Perl 5)
print(@a); # Ditto
print: @a; # Prints '012 345 6789A' followed by a newline (at least in REPL)

print  @a, @a; # Error (Two terms in a row)
print: @a, @a; # Prints '012 345 6789A 012 345 6789A' followed by a newline (in REPL)

然后我尝试在脚本文件中使用print。这对于打印到标准输出有效:

print @a;

然而, 这不会打印到标准输出:

print: @a, @a;

但是方法版本的工作良好:

$fh.print: @a, @a; # Prints '0123456789A0123456789A' to the file

我感觉我已经理解了这个, 但是不能用语言表达出来。有人可以解释下使用 print 的这些变化吗。 还有, 这些行为会因为 Great List Refactor 而改变吗?


Answer:

使用冒号代替圆括号的一个主要原因是通过移除一组圆括号,它能使代码更清晰。在其它方面它们真的一样。

当你使用 print: @a , 那你真正在做的就是在行上放置一个标签, 并让 @a 落进去(fall-through)。这在 REPL 中会调用带有值的 say 方法。

如果你没有在方法调用中使用括号或冒号,, 那么方法会以无参数方式调用。


你可以交换方法的顺序,还有调用者,如果你使用冒号的话。

say $*ERR: 'hello world'; # $*ERR.say('hello world')
我刚刚确认了, 就像你说的, print: @a 就是 label: @a, label 可以是任何东西. – Christopher Bottoms Jun 26 at 14:12
换句话说,冒号能代替方法调用的圆括号,但不能代替子例程调用。 – Christopher Bottoms Jun 26 at 14:12

5、排序散列键值对儿

my %hash =
    two   => 2,
    three => 3,
    one   => 1,
;

for %hash.sort(*.key)».kv -> ($key, $value) {
    say "'$key' => '$value'";
}

%hash.sort({.key})».kv 和上面的 sort 等价吗?

为什么这个 sort 没有 hyper » 提示就不会工作?


这个 sort方法返回一个 Pairs 的列表。

因为在列表身上调用 .kv 会返回一个索引, Pair 列表, 这不是你想要的; 你不能单单在列表身上调用 .kv 。所以你必须通过在每个 Pair 身上调用 .kv 方法分别从列表中的 Pair 中取出键和值, 这正是 ».kv 所做的。

你还可以使用 .map(*.kv) 代替。

».kv 语法允许把工作展开到多个线程中执行, 如果那样做有意义的话。

(当前的 Rakudo仅以半随机的顺序工, 以防止人们错误地使用该特性 )


通过在签名中使用副词以提取属性, 这是另一种 loop 写法:

for %hash.sort -> (:$key, :$value) {
  say "'$key' => '$value'";
}

for %hash.sort -> $pair (:$key, :$value) {
  say $pair;
  say $key === $pair.key and $value === $pair.value; # True

}

# :$key is short for :key($key)
for %hash.sort -> (:key($k), :value($v)) {
  say "'$k' => '$v'";
}

这对其它没有方法创建一组它们公用属性的对象有用:

class C { has $.a; has $.b; has $.c; has $!private-value }
my $c = 5;
my $obj = C.new(:a,:b(1),:$c);

given $obj -> ( :$a, :b($b), :$c) ) {
  say "$a $b $c";   # A 1 5
}

# ignore $.a by using an unnamed scalar
given $obj -> ( :a($), :$b, :$c ) { ... }

# places any unspecified public attributes in %others
given $obj -> ( :$a, :$b, *%others ) {
  .say for keys %others; # c

}

# 忽略任何未指定的属性
# useful to allow subclasses to add more attributes
# 或仅仅丢弃掉任何你不关心的值
given $obj -> ( :$a, :$b, *% ) { ... }

# 失败,因为它没有处理公用的 c 属性
# in the sub-signature
given $obj -> ( :$a, :$b ) { ... }

关于签名能做什么,那只是开始。

所有下面的,在子例程和方法签名中都是被允许的,非强制性的, 对于这个例子杀伤力过大。这在 multi subs 和 multi methods 中对于限制可能的候选者真的很有用。

for 'one' => 1, 1/3
->
  # Type is an alias to the object type
  ::Type Any $_ # Any is the default type requirement

  # the public attributes of the object
  (
    ::A-Type Any :key(   :numerator(   $a ) ),
    ::B-Type Any :value( :denominator( $b ) ) where $b >= 1,
  )
{
  my Type $obj = $_; # new variable declared as having the same type
  my A-Type $new-a = $a;
  my B-Type $new-b = $b;

  # could have used $_.^name or .^name instead of Type.^name
  # so you don't actually have to add the alias to the signature
  # to get the name of the arguments type
  say Type.^name, ' ', $_;
  say '  ', A-Type.^name, ' ', $a;
  say '  ', B-Type.^name, ' ', $b;
}

Pair one => 1
  Str one
  Int 1
Rat 0.333333
  Int 1
  Int 3

至于使用 .sort({.key}), 恩, 那从根本上来说是同一个东西, 因为 sort 在那儿接受任何 Callable

我要指出, 你甚至不需要为 sort 提供参数, 因为它默认比你给它的东西智能。

Perl 6 有很多创建和访问 Callable 东西的方式。所以任何下面一种都可以工作:

*.key
{ .key } # { $_.key }
-> $_ { .key } # basically what the previous line turns into
{ $^placeholder-var.key }
sub ($_) { .key }
&a-subroutine-reference # you would have to create the subroutine though

还有, 因为所有普通的操作符实际上都是子例程,你可以在需要 Callable 的其它地方使用它们:

&infix:<+> # the subroutines responsible for the numeric addition operator
&[+] # ditto

&prefix:<++>
&postfix:<++>

# etc

你可能感兴趣的:(Perl 6 Weekly(一))