Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl6: Match elements in a list with another list

Tags:

raku

I have a list of numbers L. There is another list of numbers M. I need to return a list L' of numbers found in both L and M.

Edit: Mathematically, I am looking for Multiset intersection.

Example:

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

I wrote the following code for that:

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;

While it achieves the purpose, I was wondering whether there was an idiomatic Perl6 way of doing this?

Some background: I have been trying to learn Perl6 and Python together, and solve the same puzzles in both the languages. Python offered a particularly pleasing solution for the above problem. To my beginner eyes at least :)

like image 325
Anant Avatar asked Mar 31 '17 19:03

Anant


2 Answers

You can do it with bags:

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;

Output:

bag(9, 1(2), 2)

You can turn the bag back into an array with .kxxv.

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

Output:

[9 1 1 2]

(If you don't care about duplicates, use sets instead of bags.)

like image 108
Curt Tilmes Avatar answered Dec 29 '22 15:12

Curt Tilmes


Depending on the precise semantics you're looking for, Bag operations might be just the ticket:

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

.put with L.Bag ∩ M.Bag;

displays:

9 1(2) 2

This is the stringification of a Bag containing the three keys '9', '1', and '2' whose respective values (repeat counts) are the integers 1, 2, and 1.

To get Perl 6 to produce a list from a bag with each key repeated the number of times indicated by its associated value, use the .kxxv method:

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

displays:

9 1 1 2

(The mnemonic for the kxxv method is that it's k for "key" then xx in analogy with the xx repetition operator and finally v for "value". It sorta makes sense if you think about it.)

But perhaps a bag won't do. For example, perhaps the order of elements in the result matters -- you need 9 1 2 1 and not 9 1 1 2? I'll extend this answer if a bag isn't the right way to go.

like image 36
raiph Avatar answered Dec 29 '22 14:12

raiph