raku 圣诞月历


The Flip-Flop operator


Perl5有一个二元操作符叫做flip-flop,它为假直到它的第一个参数被计算为真,然后它保持真(反转),直到第二个参数计算为真,然后在那里它又变成假(flop)。 这真是太有用了,以至于Raku也有flip-flop,只是它拼写为ff,并有一些变异:

    ff
    ff^
    ^ff
    ^ff^

音调符号^意味着在那个结尾跳过结尾。
…或许一些例子更能说明问题…

for 1..20 { .say if $_ == 9  ff  $_ == 13; }     # 9 10 11 12 13
for 1..20 { .say if $_ == 9  ff^ $_ == 13; }     # 9 10 11 12
for 1..20 { .say if $_ == 9 ^ff  $_ == 13; }     # 10 11 12 13
for 1..20 { .say if $_ == 9 ^ff^ $_ == 13; }     # 10 11 12

每个例子中,我们遍历从1到20的数字范围,并且在flip-flop返回真时输出那些数字。每次循环中,flip-flop 操作符的左边($ == 9) 和 flip-flop操作符的右边 ($ == 13)都会被计算。 (这里我已经在 flip-flop操作符的两侧使用了简单的数字比较,但是,一般任何布尔表达式都能使用。

每个 flip-flop 操作符的实例维护它们的内部状态以决定什么时候返回TRUE或False.所有的flip-flop操作符在它们的内部状态被设置为返回False时出现,直到它们被反转然后开始返回 TRUE.

在第一个和第二个例子中,当$ == 9 时,flip-flop 操作符反转它们的内部状态为 TRUE ,然后立即返回 TRUE.在第三个和第四个例子中,当$== 9时,flip-flop操作符将它们的内部状态设置为 TRUE,但是它们在那次遍历中返回 False ,因为前置的 ^符号。
类似地,在上面的第一个和第三个例子中,一旦RHS求值为真时, flip-flop操作符在下一次循环中将它们的内部状态反转回FALSE,然后返回True.在第三个和第四个例子中,flip-flop操作符在RHS返回真时立即反转为FALSE.
让flip-flop操作符反转但从不flop,在RHS上使用*:

    for 1..20 { .say if $_ == 15 ff *; }     # 15 16 17 18 19 20

Raku有另外一套 flip-flop操作符,功能与上面提到的差不多,除了,在LHS变成真的时候,RHS不被求值。这很有用,当flip-flop操作符的RHS 和LHS 都同时求值为真的时候,These operators are spelled 这些操作符被拼写为 fff, fff^, ^fff, and ^fff^.

Idiomatic Raku


December 23, 2011

下面大多数的例子使用 4 种版本展示代码:

  • Non-idiomatic Perl 5,
  • then made idiomatic.
  • Perl 5 idiom, naively translated into Raku,
  • then made idiomatic.

从 1 到 4 越来越清晰和简洁

  • 随机选择数组元素
$z = $array[ int(rand scalar(@array)) ];
$z = $array[ rand @array ];
$z = @array[ rand*@array ];
$z = @array.pick;
  • 循环遍历数组的键(索引)
for ( my $i=0; $i<@array; $i++ ) {...}
for my $i ( 0 .. $#array )       {...}
for 0 .. @array.end -> $i {...}
for @array.keys -> $i     {...}
  • 整除
( ($x - ($x % 3) ) / 3 )
int( $x / 3 )
Int( $x / 3 )   # 首字母需大写
$x div 3        # 整除运算符
  • 打印数组元素的个数
say scalar @array;
say [email protected];
say [email protected];          # Identical in Raku
say [email protected];           # + 强制新的“数值”上下文
say @array.elems;      # .elems 方法更清楚.
  • 每隔5 次 做些事情
if ( ($x/5) == int($x/5) ) {...}
if ( !($x % 5) )           {...}
if !($x % 5) {...}
if $x %% 5   {...}     # %% means "is evenly divisible by"
  • Do something $n times, 直到 $n-1
for ( $_=0; $_ < $n; $_++ ) {...}
for ( 0 .. ($n-1) )         {...}
for 0 ..^ $n {...}
for ^$n      {...}     # ^9 means 0 ..^ 9, or 0..8

eg:

> .say for ^5

0
1
2
3
4

Bare method calls are always methods on $, eliminating Perl 5’s confusion on which functions default to $.

  • 按空白分割
@words = split /s+/, $_;
@words = split;           # Default is useful, but not intuitive
@words = .split(/s+/);  # split() 现在没有默认的模式
@words = .words;         # split 的旧的行为现在成为了一个单独的方法.words
  • 将字符串分割成单独的字符
@chars = map { substr $word, $_, 1 } 0..length($word);
@chars = split '', $word;# Split on nothingness
@chars = $word.split('');
@chars = $word.comb;     # Default is to "keep everything"

eg:

> my $word='Raku'
Raku
> my @chars=$word.split('')
P e r l 6
> my @chars=$word.split('').join('->')
P->e->r->l->6
> my @chars=$word.comb
P e r l 6
> my @chars=$word.comb.join(':')
P:e:r:l:6
  • 无限循环
for (;;)  {...}    # Spoken with a 'C' accent
while (1) {...}
while 1   {...}
loop      {...}   # 没有给出限定条件,所以默认无止尽
  • 按原来的顺序返回列表中的唯一元素
my %s, @r; for @a  { push @r, $_ if !$s{$_}; $s{$_}++; } return @r;
my %s; return grep { !$s{$_}++ } @a;    # or List::MoreUtils::uniq
my %s; return grep { !%s{$_}++ }, @a;
return @a.uniq;
  • 将列表中的所有元素求和
my $sum = 0; for my $num (@a) { $sum += $num }
my $sum; $sum += $_ for @a;    # or List::Util::sum
my $sum = @a.reduce(*+*);
my $sum = [+] @a;              # [op] 将op操作符应用到整个列表
@alpha = 'A' .. 'Z';
@a = qw{ able baker charlie };
%meta = ( foo => 'bar', baz => 'quz' );
@squares = map { $_ * $_ }, @a;
@starts_with_number = grep { /^d/ }, @a;

钻石操作符还在:

  • Process each line from STDIN or from command-line files.
for my $file (@ARGV) { open FH, $file; while (<FH>) {...} }
while (<>) {...}               # Null filehandle is magical
for $*ARGFILES.lines {...}
for lines()          {...}     # lines() defaults to $fh = $*ARGFILES
  • 将散列初始化为一个常量
my %h;   for (@a) { $h{$_} = 1 }
my %h = map { $_ => 1 } @a;
my %h = map { $_ => 1 }, @a;
my %h = @a X=> 1;

eg:

> my @a=<Perl Python Ruby Raku>
Perl Python Ruby Raku
> my %h= @a X=> 1
("Perl" => 1, "Python" => 1, "Ruby" => 1, "Raku" => 1).hash
  • Hash initialization for enumeration
my %h;   for (0..$#a) { $h{ $a[$_] } = $_ }
my $c;   my %h = map  { $_ => ++$c } @a;
my $c;   my %h = map { $_ => ++$c }, @a;
("Perl" => 1, "Python" => 2, "Ruby" => 3, "Raku" => 4).hash
my %h = @a Z=> 1..*;       # ("Perl" => 1, "Python" => 2, "Ruby" => 3, "Raku" => 4).hash
my %h = @a.pairs».invert;  # if zero based , ("Perl" => 0, "Python" => 1, "Ruby" => 2, "Raku" => 3).hash

@a.pairs
0 => “Perl” 1 => “Python” 2 => “Ruby” 3 => “Raku”

  • Hash initialization from parallel arrays
my %h;   for (@a) { $h{$_} = shift @b }
my %h;   @h{@a} = @b;
my %h;   %h{@a} = @b;
my %h = @a Z=> @b;

eg:

> my @b=<Larry Gao Mztiz Larry_Wall>
Larry Gao Mztiz Larry_Wall
> my %h= @a Z=> @b
("Perl" => "Larry", "Python" => "Gao", "Ruby" => "Mztiz", "Raku" => "Larry_Wall").hash
  • 交换两个变量
my $temp = $x; $x = $y; $y = $temp;
( $x, $y ) = ( $y, $x );
 ( $x, $y )  = $y, $x;
 ( $x, $y ) .= reverse;   # .= makes reverse into a "mutating" method
 # Tastes great on array swaps, too!   @a[ $j, $k ] .= reverse;
  • Rotate array left by 1 element
my $temp = shift @a; push @a, $temp;
push @a, shift @a;
@a.push: @a.shift;
@a .= rotate; # Python Ruby Raku Perl
  • 创建一个对象
my $pet = new Dog;
my $pet = Dog->new;
my $pet = Dog.new;
my Dog $pet .= new;    # $pet *always* isa Dog; Compiler can optimize!

Combining transformation with selection was an advanced idiom in Perl 5. The new return values for if provide a bite-sized idiom.

  • Three copies of elements > 5
 @z = map { ($_) x 3 } grep { $_ > 5 } @y;    # map,grep
 @z = map { $_ > 5 ? ($_) x 3 : () } @y;      # map as grep
@z = map { $_ > 5 ?? ($_) xx 3 !! Nil }, @y;
@z = @y.map: { $_ xx 3 if $_ > 5 };          # !if == Empty list
@z = ($_ xx 3 if $_ > 5 for @y);             # List comprehension
  • 3到7之间的随机整数,包含3和7
do { $z = int rand 8 } until $z >= 3;
$z = 3 + int rand 5;
$z = 3 + Int(5.rand);
$z = (3..7).pick;
  • 在无限循环中每次循环加 3
for ( my $i = 1; ; $i++ ) { my $n = 3 * $i; ... }
for ( my $n = 3; ; $n += 3 ) {...}
loop ( my $n = 3; ; $n += 3 ) {...}
for 3, * + 3 ... * -> $n      {...}      # ... is the "sequence" operator
for 3, 6, 9 ... * -> $n       {...}      # ... can infer from example list
  • 遍历区间, 不包含开始点和结束点
for my $i ( $start .. $limit ) { next if $i == $start or $i == $limit; ... }
for my $i ( ($start+1) .. ($limit-1) ) {...}
for ($start+1) .. ($limit-1) -> $i {...}
for $start    ^..^ $limit    -> $i {...}