在 Raku 中将单词的字符简洁地转换为它的 ascii 代码列表

Convert a word's characters into its ascii code list concisely in Raku

我正在尝试将单词 wall 转换为它的 ascii 代码列表 (119, 97, 108, 108),如下所示:

my @ascii="abcdefghijklmnopqrstuvwxyz";

my @tmp;
map { push @tmp, $_.ord if $_.ord == @ascii.comb.any.ord }, "wall".comb;
say @tmp;
  1. 是否可以使用 @tmp 而无需在单独的行中声明它?

  2. 有没有办法在一行而不是 3 行中生成 ascii 代码列表?如果可以,怎么做?

请注意,我必须使用 @ascii 变量,即我不能使用连续递增的 ascii 序列 (97, 98, 99 ... 122),因为我也计划将此代码用于非 ascii 语言。

这里我们可以做几件事来让它发挥作用。

首先,我们来处理 @ascii 变量。 @ 印记表示位置变量,但您为其分配了一个字符串。这将创建一个 1 元素数组 ['abc...'],这将导致问题的发生。根据您需要的通用性,我建议直接创建数组:

my @ascii = <a b c d e f g h i j k l m n o p q r s t u v x y z>;
my @ascii = 'a' .. 'z';
my @ascii = 'abcdefghijklmnopqrstuvwxyz'.comb;

或继续处理 any 部分:

my $ascii-char = any <a b c d e f g h i j k l m n o p q r s t u v x y z>;
my $ascii-char = any 'a' .. 'z';
my $ascii-char = 'abcdefghijklmnopqrstuvwxyz'.comb.any;

这里我使用了 $ 印记,因为 any 实际上指定了任何 单个 值,因此将如此运行(这也使得我们的生活更轻松)。我个人会使用 $ascii,但我使用了一个单独的名称以使后面的示例更容易区分。

现在我们可以处理地图功能了。基于以上两个版本的ascii,我们可以将你的map函数重写为以下任意一个

{ push @tmp, $_.ord if $_ eq @ascii.any  }
{ push @tmp, $_.ord if $_ eq $ascii-char }

请注意,如果您更喜欢使用 ==,您可以继续在最初的 ascii 创建中创建数值,然后使用 $_.ord。同样,就我个人而言,我喜欢命名映射变量,例如:

{ push @tmp, $^char.ord if $^char eq @ascii.any  }
{ push @tmp, $^char.ord if $^char eq $ascii-char }

其中 $^foo 替换 $_(如果您使用多个,它们会将字母顺序映射为 @_[0]@_[1] 等)。

但让我们来谈谈更有趣的问题。我们如何在不需要预先声明 @tmp 的情况下完成所有这些操作?显然,这只需要在 map 循环中创建数组。当我们没有 ASCII 值时,您可能认为这可能很棘手,但事实上 if 语句 returns Empty (或 () )如果没有运行 让生活变得轻松:

my @tmp = map { $^char.ord if $^char eq $ascii-char }, "wall".comb;
my @tmp = map { $^char.ord if $^char eq @ascii.any  }, "wall".comb;

如果我们使用“wáll”,map收集的列表将是119, Empty, 108, 108,它会自动返回119, 108, 108。因此,@tmp 仅设置为 119, 108, 108.

是的,有一个更简单的方法。

"wall".ords.grep('az'.ords.minmax);

当然这依赖于az是一个完整的序列。这是因为 minmax 根据列表中的最小值和最大值创建了一个 Range 对象。

如果它们不在一个完整的序列中,您可以使用一个连接点。

"wall".ords.grep( 'az'.ords.minmax | 'AZ'.ords.minmax );

但是你说要匹配其他语言。这对我来说是正则表达式。

"wall".comb.grep( /^ <:Ll> & <:ascii> $/ ).map( *.ord )

这与也在 ASCII 中的小写字母相匹配。

其实我们可以让它变得更简单。 comb 可以采用正则表达式来确定从输入中获取哪些字符。

"wall".comb( / <:Ll> & <:ascii> / ).map( *.ord )
# (119, 97, 108, 108)

"ΓΔαβγδε".comb( / <:Ll> & <:Greek> / ).map( *.ord )
# (945, 946, 947, 948, 949)
# Does not include Γ or Δ, as they are not lowercase

请注意,如果您没有组合重音符号,以上内容仅适用于 ASCII。

 "de\c[COMBINING ACUTE ACCENT]f".comb( / <:Ll> & <:ascii> / )
 # ("d", "f")

Combining Acute Accent 与 e 相结合,组成带有尖音符号的拉丁文小写字母 E。 该组合字符不在 ASCII 中,因此被跳过。

如果字符没有组合值,那就更奇怪了。

"f\c[COMBINING ACUTE ACCENT]".comb( / <:Ll> & <:ascii> / )
# ("f́",)

那是因为 f 是小写的并且是 ASCII 格式的。不过,组合代码点会随身携带。

基本上,如果您的数据具有或可能具有组合重音,并且它可能会破坏事物,那么您最好在它仍处于二进制形式时处理它。

$buf.grep: {
    .uniprop() eq 'Ll' #
    && .uniprop('Block') eq 'Basic Latin' # ASCII
}

以上也适用于单个字符串,因为 .uniprop 适用于表示代码点的整数或实际字符。

"wall".comb.grep: {
    .uniprop() eq 'Ll' #
    && .uniprop('Block') eq 'Basic Latin' # ASCII
}

再次注意,这与组合代码点有同样的问题,因为它适用于字符串。

您可能还想使用 .uniprop('Script') 而不是 .uniprop('Block'),具体取决于您想要做什么。

这是一个使用 Raku 的 trans 方法的工作方法(在 Raku REPL 中执行的代码片段):

> my @a = "wall".comb;
[w a l l]
> @a.trans('abcdefghijklmnopqrstuvwxyz' => ords('abcdefghijklmnopqrstuvwxyz') ).put;
119 97 108 108

上面,我们处理了一个ascii字符串。下面我添加了“é”字符,并显示了一个两步解决方案:

> my @a = "wallé".comb;
[w a l l é]
> my @b = @a.trans('abcdefghijklmnopqrstuvwxyz' => ords('abcdefghijklmnopqrstuvwxyz') );
[119 97 108 108 é]
> @b.trans("é" => ords("é")).put
119 97 108 108 233

Nota bene #1:虽然上面的所有代码都可以正常工作,但是当我尝试将字母表缩短为 'a'..'z' 时,我最终看到了错误的 return 值...因此使用了完整的'abcdefghijklmnopqrstuvwxyz'.

注意事项 #2:我想到的一个问题是在 trans 无法识别字符时试图抑制输出(例如,如何抑制将“é”分配为 [=17= 的最后一个元素) ] 在上面的 second-example 代码中)。我尝试将 :delete 参数添加到 trans,但没有成功。

已编辑: 要删除不需要的字符,这里的代码使用 grep(@Brad Gilbert),然后是 trans

> my @a = "wallé".comb;
[w a l l é]
> @a.grep('a'..'z'.comb.any).trans('abcdefghijklmnopqrstuvwxyz' => ords('abcdefghijklmnopqrstuvwxyz') ).put
119 97 108 108