【问题标题】:Perl6: Match elements in a list with another listPerl6:将列表中的元素与另一个列表匹配
【发布时间】:2017-03-31 19:06:56
【问题描述】:

我有一个数字列表L。还有另一个数字列表M。我需要返回 LM 中找到的数字列表 L'

编辑:数学上,我正在寻找Multiset 交点。

例子:

L = 3, 1, 4, 1, 5, 9, 2, 6
M = 9 , 7, 1, 2, 1, 1
L' = 9, 1, 2, 1

为此我写了following code

my @some-numbers = 3, 1, 4, 1, 5, 9, 2, 6;
my @to-match     = 9, 7, 1, 2, 1, 1;
my @matched;

my %histogram;
for @some-numbers -> $n { %histogram{$n}++ };

for @to-match -> $n {
    next if not defined %histogram{$n};
    if %histogram{$n} > 0 {
        push @matched, $n;
        %histogram{$n}--;
    }
};

say @matched;

虽然它达到了目的,但我想知道是否有 惯用的 Perl6 方法可以做到这一点?

一些背景知识:我一直在尝试一起学习 Perl6 和 Python,并用这两种语言解决相同的难题。 Python 为上述问题提供了一个特别的pleasing solution。至少在我的初学者眼中:)

【问题讨论】:

  • 为什么1L' 中出现了两个,而不是三个?
  • 在 Perl5 中你可以做 my %seen = map { $_ => 1 } @to_match; my @match = grep { exists $seen{$_} } @some_numbers
  • 很抱歉没有准确地提出问题。我现在意识到我正在寻找的是Multisetintersection。我已经编辑了问题陈述以反映这一点。谢谢:)

标签: raku


【解决方案1】:

你可以用包来做:

my $some-numbers = bag 3, 1, 4, 1, 5, 9, 2, 6;
my $to-match     = bag 9, 7, 1, 2, 1, 1;
my $matched      = $some-numbers ∩ $to-match;
say $matched;

输出:

bag(9, 1(2), 2)

您可以使用.kxxv 将包转回数组。

my @match-list = $matched.kxxv;
say @match-list;

输出:

[9 1 1 2]

(如果您不关心重复,请使用集合而不是袋子。)

【讨论】:

  • 对于遇到 Unicode 运算符问题的任何人, 的德克萨斯版本是 (&)
【解决方案2】:

根据您正在寻找的精确语义,Bag 操作可能只是门票:

my \L = 3, 1, 4, 1, 5, 9, 2, 6;
my \M = 9, 7, 1, 2, 1, 1;

.put with L.Bag ∩ M.Bag;

显示:

9 1(2) 2

这是 Bag 的字符串化,包含三个 '9''1''2',它们各自的 (重复计数)是整数 121

要让 Perl 6 从包中生成一个列表,其中每个键重复其关联值指示的次数,请使用 .kxxv 方法:

.kxxv.put with L.Bag ∩ M.Bag;

显示:

9 1 1 2

kxxv 方法的助记符是 k 表示“键”,然后 xx 类似于 xx 重复运算符,最后是 v 表示“值”。如果你考虑一下。)

但也许一个包是不行的。例如,结果中元素的顺序可能很重要——您需要9 1 2 1 而不是9 1 1 2?如果袋子不是正确的选择,我会扩展这个答案。

【讨论】:

  • 如果“9 1 2”是一个可以接受的答案,你实际上不需要先强制到 Bag,你可以取两个列表的交集:“L (&) M” (使用德克萨斯版本的∩)。交集默认使用 Set 语义。
  • 包包非常适合我!从交集返回的元素顺序无关紧要。
【解决方案3】:

你可以试试这个:

use v6;

my @some_numbers = 3, 1, 4, 1, 5, 9, 2, 6;
my @to_match     = 9, 7, 1, 2, 1, 1;
my %seen  = map { $_ => 1 }, @to_match;
my @matched = grep { %seen{$_}:exists }, @some_numbers;
say @matched;

输出

[1 1 9 2]

【讨论】:

  • 看起来有点像 Perl5ish .. 你可以使用 @to_match X=> 1; 来构建 %seen 哈希而不是地图,或者完全放弃 %seen 哈希并使用 @some_numbers.grep: { @to_match.first($_) };
  • 如果第一个列表中有额外元素,则无法正常工作:@some_numbers = 3, 1, 4, 1, 5, 9, 2, 6, 1; @to_match = 9, 7, 1, 2, 1
猜你喜欢
  • 2019-01-05
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 2021-07-12
  • 2018-04-15
  • 2019-08-31
  • 2018-02-25
相关资源
最近更新 更多