如何循环 Raku 中散列的排序(使用自定义排序)键?

use*_*022 10 raku

尝试逐步将一些 Perl 脚本转换为 Raku。即使在这里浏览了很多内容并更深入地阅读了Learning Perl 6,我仍然坚持下面的内容。

我无法取得进展的部分是最后一个循环(转换为for);获取密钥并按月份名称和日期编号对它们进行排序看起来是不可能的,但我相信这是可行的。

任何有关如何使用“惯用”语法实现这一目标的提示都将非常受欢迎。

#!/usr/bin/perl

use strict;

my %totals;

while (<>) {
    if (/redis/ and /Partial/) {
        my($f1, $f2) = split(' ');
        my $w = $f1 . ' ' . $f2;
        $totals{$w}++;
    }
}

my %m = ("jan" => 1, "feb" => 2, "mar" => 3, "apr" => 4, "may" => 5, "jun" => 6,
         "jul" => 7, "aug" => 8, "sep" => 9, "oct" => 10, "nov" => 11, "dec" => 12);

foreach my $e (sort { my($a1, $a2) = split(' ', $a) ; my($b1, $b2) = split(' ', $b) ;
            $m{lc $a1} <=> $m{lc $b1} or $a2 <=> $b2 } keys %totals) {
    print "$e", " ", $totals{$e}, "\n";
}
Run Code Online (Sandbox Code Playgroud)

Hol*_*lli 10

输入相同的样本数据,您的 perl 代码会产生与此相同的输出。

my $data = q:to/END/; 
may 01 xxx3.1 Partial redis
may 01 xxx3.2 Partial redis
may 01 xxx3.3 Partial redis
apr 22 xxx2.2 Partial redis
apr 22 xxx2.1 Partial redis
mar 01 xxx1 redis Partial
some multi-line
string
END


sub sort-by( $value )
{
  state %m = <jan feb  mar apr may jun jul aug sep oct nov dec> Z=> 1..12;
  %m{ .[0].lc }, .[1] with $value.key.words;
}

say .key, ' ', .value.elems 
  for $data
    .lines
    .grep( /redis/ & /Partial/ )
    .classify( *.words[0..1].Str )
    .sort( &sort-by );
Run Code Online (Sandbox Code Playgroud)


wam*_*mba 8

你可以尝试这样的事情:

enum Month (jan => 1, |<feb mar apr may jun jul aug sep oct nov dec>);

lines()
andthen .grep: /redis/&/Partial/
andthen .map: *.words
andthen .map: {Month::{.[0].lc} => .[1].Int}\ 
#or andthen .map: {Date.new: year => Date.today.year, month =>  Month::{.[0].lc},  day => .[1], }\
andthen  bag $_
andthen .sort
andthen .map: *.put;
Run Code Online (Sandbox Code Playgroud)


p6s*_*eve 6

我认为这与你所要求的很接近......也表明 perl6/raku 与 perl5 非常密切相关,除非你想变得幻想......

#test data...
my %totals = %( 
    "jan 2" => 3,
    "jan 4" => 1,
    "feb 7" => 1,
);

my %m = %("jan" => 1, "feb" => 2, "mar" => 3, "apr" => 4, "may" => 5, "jun" => 6,
         "jul" => 7, "aug" => 8, "sep" => 9, "oct" => 10, "nov" => 11, "dec" => 12);

my &sorter = { 
    my ($a1, $a2) = split(' ', $^a); 
    my ($b1, $b2) = split(' ', $^b);
    %m{lc $a1} <=> %m{lc $b1} or $a2 <=> $b2 
}

for %totals.keys.sort(&sorter) ->$e {
    say "$e => {%totals{$e}}" 
}

#output
jan 2 => 3
jan 4 => 1
feb 7 => 1
Run Code Online (Sandbox Code Playgroud)

主要变化有:

  • %totals{$e} 为 $totals{$e}
  • %() 而不是 {} 用于哈希文字
  • for with 方法语法和 -> 而不是 foreach with sub 语法
  • 排序例程中的 $^a 和 $^b 需要插入符号 (^)
  • 说比印刷品干净一点


rai*_*iph 6

TL;DR @wamba 提供了一个惯用的解决方案。这个答案是一个最小的“机械”翻译。

我认为您的问题和这个答案表明,学习 Raku 与 Perl 相关的许多基础知识的一种好方法是:

  1. 将一个小的 Perl 程序输入 Rakudo;

  2. 有条不紊地调查/修复每个报告的错误,直到它起作用;

  3. 如果遇到问题,请向 StackOverflow 提出问题。

假设这就是你所做的,太好了。如果没有,希望这个答案能激励你或其他读者尝试这样做。

编码

my %totals;

for lines() {
    if (/redis/ and /Partial/) {
        my ($f1, $f2) = split(' ', $_);
        my $w = $f1 ~ ' ' ~ $f2;
        %totals{$w}++;
    }
}

my %m = ("jan" => 1, "feb" => 2, "mar" => 3, "apr" => 4, "may" => 5, "jun" => 6,
         "jul" => 7, "aug" => 8, "sep" => 9, "oct" => 10, "nov" => 11, "dec" => 12);

for sort { my ($a1, $a2) = split(' ', $^a) ; my ($b1, $b2) = split(' ', $^b) ;
            %m{lc $a1} <=> %m{lc $b1} or $a2 <=> $b2 }, keys %totals
    -> $e {
    print "$e", " ", %totals{$e}, "\n";
}
Run Code Online (Sandbox Code Playgroud)

适用于以下测试输入:

feb 1 redis Partial
jan 2 Partial redis
jan 2 redis Partial
Run Code Online (Sandbox Code Playgroud)

机械翻译过程

通过将您问题中的代码提供给 Rakudo,我开始正确处理您的问题。并惊讶地得到Unsupported use of <>. ...。这是 Perl 代码,而不是 Rakunian!

然后我看到@wamba 提供了一个惯用的解决方案。我决定做最直接的翻译。我的第一次尝试成功了。秩序恢复。

我思考如何最好地解释我的变化。我想知道如果我回到起点并且一次只修复一个错误消息会是什么。结果是一系列令人愉快的错误消息。因此,我将这个答案的其余部分构建为一系列错误消息/修复/讨论,每一个都会导致下一个,直到程序正常运行。

为了简单起见,我从错误消息中删除了大部分信息。消息/修复的顺序是我遇到的顺序,一次修复一个:


  1. Unsupported use of <>.  In Raku please use: lines() to read input ...
    ------> while (<?>) {
    
    Run Code Online (Sandbox Code Playgroud)

    ?是 Unicode 的弹出符号,标志着编译器在概念上“弹出”代码的点。)

    Perl 的惯用替换while (<>)for lines().


  1. Variable '$f1' is not declared
    ------>         my(?$f1, $f2) ...
    
    Run Code Online (Sandbox Code Playgroud)

    foo(...)如果函数调用有意义,Raku 会将表单的代码解释为函数调用。这优先于解释foo为关键字(即my作为变量声明符)。

    接下来,由于my($f1, $f2)被解释为函数调用,因此$f1被解释为您尚未声明的参数,从而导致错误消息。

    my修复真正的问题和这个明显的问题之后插入空格。

    (此错误发生在您代码中的多个位置;我每次都应用相同的修复程序。)


  1. Unsupported use of .  to concatenate strings.  In Raku please use: ~.
    ------>         my $w = $f1 .? ' ' . $f2;
    
    Run Code Online (Sandbox Code Playgroud)

    为了帮助记住它~在 Raku 中用作字符串操作,请注意它看起来像一段字符串。


  1. Variable '$totals' is not declared.  Did you mean '%totals'?
    ------>         ?$totals{$w}++;
    
    Run Code Online (Sandbox Code Playgroud)
    • 正如达米安·康威 (Damian Conway) 所指出的那样,“我们采用了这张 Perl 表来说明您何时使用什么,而我们将其改为这张表”

    • 该代码$totals{...}语法上是有效的。一个可以结合或散列(参考)分配给一个标量。但是 Rakudo(Raku 编译器)在编译时知道代码没有声明$totals变量,所以它正确地抱怨。

    • 您的代码已经声明了一个%totals变量,因此 Rakudo 会很有帮助地询问您是否是这个意思。

    (此错误发生在您代码中的多个位置;我每次都应用相同的修复程序。)


  1. Unsupported use of 'foreach'.  In Raku please use: 'for'.
    ------> foreach? my 
    
    Run Code Online (Sandbox Code Playgroud)

    Raku 代码往往比 Perl 代码更短(也更易读)。这主要是由于设计超越了单纯的油漆,但像s/for/foreach不伤害这样的小事。


  1. This appears to be Perl code
    ------> for ?my $e 
    
    Run Code Online (Sandbox Code Playgroud)

    此错误消息可以说是LTA(我认为是“非描述性”)。但考虑到所有因素,它同样可以说相当不错。

    Perl 和 Raku 支持将值绑定到范围为块的新词法变量/参数。Perl 使用my, 并将变量放在值之前。Raku 将值放在首位,->在它们和任何变量之间插入 a ,然后跳过my.

    (这种用法有很多丰富之处->,我不会在这里介绍,因为这对这个例子来说无关紧要。但值得一提的是,这种变化为 Rakoons 带来了很多好处,你已经明白了期待。)


  1. Variable '$a' is not declared
    ------> for sort { my ($a1, $a2) = split(' ', ?$a)
    
    Run Code Online (Sandbox Code Playgroud)

    Perl 开发人员知道,它具有特殊的变量$a$b.

    Raku 将这个概念概括为$^fooparameters,这是一种向闭包添加位置参数的便捷DRY方式,同时跳过通常必须指定名称两次的仪式(一次声明,另一次使用它)。

    其中一个不寻常的方面,最初可能看起来很疯狂,但实际上非常符合人体工程学,它们的形式参数位置由它们的名称决定。因此,给定$^fooand $^bar,后者将绑定到第一个位置参数,$^foo第二个。


  1. Missing comma after block argument to sort
    ------> { ... }? keys
    
    Run Code Online (Sandbox Code Playgroud)

    我在指示的地方插入了一个逗号。


  1. Calling split(Str) will never work with signature of the proto ($, $, |)
    ------>         my ($f1, $f2) = ?split(' '
    
    Run Code Online (Sandbox Code Playgroud)

    一些 Perl 例程隐含地假定使用$_. 没有语法方法可以知道任何给定的例程是否隐式使用它。你只需要阅读每个例程的定义。乐放弃了。

    因此 Rakudo 得出结论,split例程缺少要拆分的字符串。

    |“proto ($, $, |)”中的“签名”仅表示“可以选择传递的其他参数”,因此您可以忽略它。$, $表示需要两个参数,因此我们缺少一个。)

    的常规定义显示了快速检查sub的版本split需要字符串被分裂作为其第二个位置参数。因此我切换到split(' ', $_).


  1. 该代码有效。\o/

    值得注意的是,所有实际的错误消息都以===SORRY!=== Error while compiling. 这意味着它们都在程序运行之前就被捕获了,这很好。

  • 谢谢你的提问。多写点!虽然你已经得到了很好的答案并接受了 @wamba 的简单明了的答案,而且 Raku 的光芒部分归功于 20 年来细心、充满爱心的日常 [碳酸氢盐浴](https://www.google.com/search?q =timtoady+碳酸氢盐),思考事物的方式总是不止一种,而这种精神在 Raku 文化和 SO 中依然存在。这让我今天想要制定自己的解决方案,如果觉得值得添加,请发布我的第二个答案。欢迎使用“[raku]”标签。 (3认同)