perl6 What is the best way to match any of a group of words? - match

I am trying to find a simple way to match any of a group of words. I have been using a for loop, but is there a simpler way?
my #a=<a b c d e f>;
my $x="a1234567";
say $x ~~ m/ #a.any /;
It returns False. Is there a way to make it work? Thanks.

my #a = <a b c d e f>;
my $x = "a1234567";
say $x ~~ /#a/;
/#a/ is the same as /| #a/ which is longest alternation. For alternation you can use /|| #a/.

Related

Splitting an Array into n accessible parts within perl?

My goal is to take an array of letters and cut it up into "n" parts. In this case no more than 10 letters each piece. But I want these arrays to be stored into an array reference which I can access on a counter.
For example, I have the following script to split an array of English alphabetical letters into 1 array of 10 letters. But since the English Alphabet has 26 letters, I need 2 more arrays to access in an array reference.
#!/usr/bin/env perl
#split an array into parts.
use strict;
use warnings;
use feature 'say';
my #letters = ('A' .. 'Z');
say "These are my letters:";
for(#letters){print "$_ ";}
my #letters_selected = splice(#letters, 0, 10);
say "\nThese are my selected letters:";
for(#letters_selected){print "$_ ";}
The output is this:
These are my letters:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
These are my selected letters:
A B C D E F G H I J
This little script only gives me one piece of 10 letters of the alphabet. But I want all three pieces of 10 letters of the alphabet, so I would like to know how I can achieve this:
Goal:
Have an array reference called letters_selected of letters which contains all letters A - Z. But ... I can access all three pieces of size less than or equal to 10 letters like this.
foreach(#{$letters_selected[0]}){say "$_ ";}
returns: A B C D E F G H I J # These are the initial 10 elements of the alphabet.
foreach(#{$letters_selected[1]}){say "$_ ";}
returns: K L M N O P Q R S T # The next 10 after that.
foreach(#{$letters_selected[2]}){say "$_ ";}
returns: U V W X Y Z # The next no more than 10 after that.
Since splice is destructive to its target you can keep applying it
use warnings;
use strict;
use feature 'say';
my #letters = 'A'..'Z';
my #letter_groups;
push #letter_groups, [ splice #letters, 0, 10 ] while #letters;
say "#$_" for #letter_groups;
After this #letters is empty. So make a copy of it and work with that if you will need it.
Every time through, splice removes and returns elements from #letters and [ ] makes an anonymous array of that list. This reference is pushed on #letter_groups.
Since splice takes as many elements as there are (if there aren't 10) once fewer than 10 remain splice removes and returns that, the #letters gets emptied, and while terminates.

sort uniq(#stuff) doesn't sort, doesn't de-dupe [duplicate]

This question already has answers here:
why does sort with uniq not work together
(2 answers)
Closed 6 years ago.
Some code has me waxing lyrical,
The outcome is slightly hysterical.
sorted output I seek,
All elements unique,
But the results? Far from clinical.
Code in question
use strict;
use warnings;
sub uniq { my %seen; grep ! $seen{$_}++, #_ }
my #test = ();
for ( 1 .. 3 ) {
#test = sort uniq( #test, qw/ d d c c b b a a / );
print "#test\n";
}
Output
d d c c b b a a
d d c c b b a a d d c c b b a a
d d c c b b a a d d c c b b a a d d c c b b a a
The Fix
An extra set of parentheses restores parity:
#test = sort( uniq( #test, qw/ d d c c b b a a / ) ); # a b c d
Running the two lines through -MO=Deparse sheds some light on the effect of the extra parens - it forces the interpreter to treat the RHS as sort LIST instead of sort SUBNAME LIST:
# Doesn't work as intended (sort SUBNAME LIST)
#test = (sort uniq #test, ('d', 'd', 'c', 'c', 'b', 'b', 'a', 'a'));
# Works as intended (sort LIST)
#test = sort(uniq(#test, ('d', 'd', 'c', 'c', 'b', 'b', 'a', 'a')));
My Question
Why is the extra set of parentheses necessary?
uniq returns a list, so I'd expect
sort uniq( #stuff );
to be equivalent to
sort LIST
Although it's rarely used, the first form listed in perldoc -f sort is sort SUBNAME LIST. i.e. the optional second argument to sort is the name of a function to use as the sort comparator. The LIST, of course, may or may not have parentheses as it wants, and whitespace is free, so
sort uniq( #test, qw/ d d c c b b a a / )
means to sort the list (#test, qw/ d d c c b b a a /) with the function uniq as a comparator. Since the result of uniq is independent of $a and $b and it has no prototype, it always returns undef, which sort considers as 0, and sort responds to this assertion that everything is equal by not changing the order of anything (since it's a stable sort, since 5.8 at least).
uniq treated as a sub name because it's an identifier or a qualified identifier that's not also a function name. No actual check is made to see if the sub actually exists (although it would have found the sub to exist in this case).
sort needs to be followed by a function name or something that's not an identifier or qualified identifier to be disqualified from the sort SUBNAME LIST syntax.

Test a variable against a set of values

In perl, what's the best way of testing a variable against multiple values?
Something like this (in pseudo code):
if x is in {'q','w','e','r','t'}
# do something
This is what the "new" smart match operator could be used for:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.10.0;
my $thing = 'bar';
my #set = qw(foo bar baz);
say 'ok!' if $thing ~~ #set;
Output:
ok!
Note: this isn't available in ancient versions of perl. Before 5.10, you want to use grep as in the other answer, or, if your set is very big, first of List::Util because it wouldn't iterate over the whole list after the first match.
How about:
if (grep /^x$/, ('q', 'w','e','r','t')) {
# Do something
}
This works if the values you are comparing are scalars (strings or numbers).
For strings, there is a nice shorthand:
if (grep /^x$/, qw(q w e r t y)) {
# Do something
}
If you don't like the regex notation (/^x$/), there is:
grep {$_ eq 'x'} qw(q w e r t y)
Where you can use $_ to test for anything, not just equality.
If what you want to do is simple (can be expressed in a line), just this will do:
do_something if grep /^x$/, qw(q w e r t y)

Apply regexp replace only to quoted piece

I need to apply a regexp filtration to affect only pieces of text within quotes and I'm baffled.
$in = 'ab c "d e f" g h "i j" k l';
#...?
$inquotes =~ s/\s+/_/g; #arbitrary regexp working only on the pieces inside quote marks
#...?
$out = 'ab c "d_e_f" g h "i_j" k l';
(the final effect can strip/remove the quotes if that makes it easier, 'ab c d_e_f g...)
You could figure out some cute trick that looks like line noise.
Or you could keep it simple and readable, and just use split and join. Using the quote mark as a field separator, operate on every other field:
my #pieces = split /\"/, $in, -1;
foreach my $i (0 ... $#pieces) {
next unless $i % 2;
$pieces[$i] =~ s/\s+/_/g;
}
my $out = join '"', #pieces;
If you want you use just a regex, the following should work:
my $in = q(ab c "d e f" g h "i j" k l);
$in =~ s{"(.+?)"}{$1 =~ s/\s+/_/gr}eg;
print "$in\n";
(You said the "s may be dropped :) )
HTH,
Paul
Something like
s/\"([\a\w]*)\"/
should match the quoted chunks. My perl regex syntax is a little rusty, but shouldn't just placing quote literals around what you're capturing do the job? You've then got your quoted string d e f inside the first capture group, so you can do whatever you want to it... What kind of 'arbitrary operation' are you trying to do to the quoted strings?
Hmm.
You might be better off matching the quoted strings, then passing them to another regex, rather than doing it all in one.

Why do I get an error when I try to use the reptition assignment operator with an array?

#!/usr/bin/perl
use strict;
use warnings;
my #a = qw/a b c/;
(#a) x= 3;
print join(", ", #a), "\n";
I would expect the code above to print "a, b, c, a, b, c, a, b, c\n", but instead it dies with the message:
Can't modify private array in repeat (x) at z.pl line 7, near "3;"
This seems odd because the X <op>= Y are documented as being equivalent to X = X <op> Y, and the following code works as I expect it to:
#!/usr/bin/perl
use strict;
use warnings;
my #a = qw/a b c/;
(#a) = (#a) x 3;
print join(", ", #a), "\n";
Is this a bug in Perl or am I misunderstanding what should happen here?
My first thought was that it was a misunderstanding of some subtlety on Perl's part, namely that the parens around #a made it parse as an attempt to assign to a list. (The list itself, not normal list assignment.) That conclusion seems to be supported by perldiag:
Can't modify %s in %s
(F) You aren't allowed to assign to the item indicated, or otherwise try to
change it, such as with an auto-increment.
Apparently that's not the case, though. If it were this should have the same error:
($x) x= 3; # ok
More conclusively, this gives the same error:
#a x= 3; # Can't modify private array in repeat...
Ergo, definitely a bug. File it.
My guess is that Perl is not a language with full symbolic transformations. It tries to figure out what you mean. If you "list-ify" #a, by putting it in parens, it sort of loses what you wanted to assign it to.
Notice that this does not do what we want:
my #b = #a x 3; # we'll get scalar( #a ) --> '3' x 3 --> '333'
But, this does:
my #b = ( #a ) x 3;
As does:
( #a ) = ( #a ) x 3;
So it seems that when the expression actally appears on both sides Perl interprets them in different contexts. It knows that we're assigning something, so it tries to find out what we're assigning to.
I'd chalk it up to a bug, from a very seldom used syntax.
The problem is that you're trying to modify #a in place, which Perl evidently doesn't allow you to do. Your second example is doing something subtly different, which is to create a new array that consists of #a repeated three times, then overwriting #a with that value.
Arguably the first form should be transparently translated to the second form, but that isn't what actually happens. You could consider this a bug... file it in the appropriate places and see what happens.