perl6 Hash key <$/[0]> , <"$/[0]"> and {"$/[0]"} make the values behave differently - hash

I have a file in the format of type : amount and I want to put all
the lines into a hash whose keys are the types and the values are
the sum of the amounts.
However, it seems that how the types are quoted affects how the
amounts behave. Can someone explain how so?
Here is the contents of file data:
food : 100.01
gas : 50.02
food : 20.03
gas : 30.04
When the keys are constructed with %x<$/[0]> , the keys seem to be
correct, but the values seem to be pushed into an array, even though I
want to add to get the total amount:
home/lisprog$ cat data | perl6 -ne '
state %x //= {};
if $_ ~~ m/ \s* (\S+) \s* \: \s* (\d+\.\d+) \s* / {
if %x<$/[0]>:exists {
%x<$/[0]> += ($/[1].Numeric);
}
else {
%x.push: "$/[0]"=>($/[1].Numeric);
}
};
END { for %x.kv -> $m, $n { say "$m : $n"; } }' | sort
food : 100.01 20.03
gas : 50.02 30.04
The same behavior if the keys are constructed with <"$/[0]"> :
cat data | perl6 -ne '
state %x //= {};
if $_ ~~ m/ \s* (\S+) \s* \: \s* (\d+\.\d+) \s* / {
if %x<"$/[0]">:exists {
%x<"$/[0]"> += ($/[1].Numeric);
}
else {
%x.push: "$/[0]"=>($/[1].Numeric);
}
};
END { for %x.kv -> $m, $n { say "$m : $n"; } }' | sort
food : 100.01 20.03
gas : 50.02 30.04
When the keys are constructed with %x{"$/[0]"}, the keys and values
behave correctly:
home/lisprog$ cat data | perl6 -ne '
state %x //= {};
if $_ ~~ m/ \s* (\S+) \s* \: \s* (\d+\.\d+) \s* / {
if %x{"$/[0]"}:exists {
%x{"$/[0]"} += ($/[1].Numeric);
}
else {
%x.push: "$/[0]"=>($/[1].Numeric);
}
};
END { for %x.kv -> $m, $n { say "$m : $n"; } }' | sort
food : 120.04
gas : 80.06
So, how come the keys affect how values behave? Or did I ask a wrong question?

I think you have a misconception about what <> does. They're basically the equivalent of single quotes:
say <"foo"> # "foo"
So you're checking if a key '$/[0]' exists, which it doesn't, and then push a Pair onto the hash with the correct double quoted (although unnecessary to quote it there), which will create the correct key if it doesn't exist yet.
Since $0 is short for $/[0], m/// uses $_ by default, and non-existing keys in a hash are silently created with Any, and you can += Any just like you can in Pumpkin Perl 5, and END blocks don't need to have a block, you only really need:
$ cat data | perl6 -ne '
state %x;
%x{$0} += $1 if m/ \s* (\S+) \s* \: \s* (\d+\.\d+) \s* /;
END for %x.kv -> $m, $n { say "$m : $n" }' | sort
Actually, come to think of it, the actually smells like a Mix (https://docs.raku.org/type/Mix). Leaving that as an exercise for the reader :-)

Related

How to tell if a string can be transformed into another string by removing or uppercasing lowercase letters?

In perl, I have two input strings, for this example, ahueFFggLKy and HFFGLK. I want to be able to iterate through all of the possible combinations of my input without lowercase letter groups (a, h, u, e, g...ah, au...hegy, etc) so in each iteration lowercase letters are removed and the remaining lowercase letters are uppercased:
ah:
ueFFggLKy (UEFFGGLKY)
^^
au:
h eFFggLKy (HEFFGGLKY)
^ ^
hegy:
a u FF gLKy | a u FFg LKy (AUFFGLKY)
^ ^ ^ | ^ ^ ^
auegy:
h FF gLK | h FFg LK (HFFGLK)
^ ^^ ^ ^ ^ ^^ ^ ^ -^--^-
The last option (auegy) is the answer, and I want to be able to iterate over letters to determine if I am able to convert ahueFFggLKy to HFFGLK without modifying any of the capital letters. This example would return "YES".
If inputs like fOoBar and BAR come up, I am not successfully able to convert fOoBar to BAR because the O in fOoBar is capitalized. My program would return "NO".
Can someone provide me with a perl example of how this would be done?
I think I have understood your requirement: the first string may be transformed by either deleting or upper-casing any lower-case letter, and you wish to know whether the second string can be derived from the first in this way
I suggest that you can transform the second string to a regex pattern to achieve this. If every upper-case letter in the second string must be matched by the corresponding upper or lower-case letter in the first, with any number of intervening lower-case letters, then the transformation is possible. Otherwise it is not
This program implements the idea
use strict;
use warnings 'all';
use feature 'say';
my #pairs = (
[ qw/ ahueFFggLKy HFFGLK / ],
[ qw/ fOoBar BAR / ],
);
for my $pair ( #pairs ) {
my ($s1, $s2) = #$pair;
printf "%s => %s -- %s\n", $s1, $s2, contains($s1, $s2) ? 'YES' : 'NO';
}
sub contains {
my ($s1, $s2) = #_;
my $re = join ' \p{Ll}* ', map { "(?i: $_ )" } $s2 =~ /\p{Lu}/g;
$re = qr/ ^ \p{Ll}* $re \p{Ll}* $ /x;
$s1 =~ $re;
}
output
ahueFFggLKy => HFFGLK -- YES
fOoBar => BAR -- NO
To read an array like #pairs from STDIN you could write something like this
my #pairs;
{
local $/;
my #input = split ' ', <>;
push #pairs, [ splice #input, 0, 2 ] while #input > 1;
}
Kind of unelegant solution, but it seems to output what you need.
#!/usr/bin/perl
use warnings;
use strict;
use List::Util qw{ all };
my ($str1, $str2) = qw( ahueFFggLKy HFFGLK );
my #small_indices;
push #small_indices, pos($str1) - 1 while $str1 =~ /[[:lower:]]/g;
my #present = (0) x #small_indices;
until (all { $_ } #present) {
my $try = $str1;
for my $i (reverse 0 .. $#present) {
substr $try, $small_indices[$i], 1,
$present[$i] ? substr $str1, $small_indices[$i], 1
: q();
}
if (uc $try eq $str2) {
print $present[$_] ? q() : substr $str1, $small_indices[$_], 1
for 0 .. $#present;
print ":\n";
my $j = 0;
for my $i (0 .. length($str1) - 1) {
my $char = substr $str1, $i, 1;
if ($char eq uc $char || $present[$j++]) {
print $char;
} else {
print '.';
}
}
print "\n";
}
my $idx = 0;
$present[$idx] = 0, ++$idx while $present[$idx];
$present[$idx] = 1;
}
It builds an indicator function #present, which say what lowercase letters are present in the string. All possible values of #present are iterated by adding 1 to the binary number corresponding to the function.

How do match the two strings which contains parentheses in perl

How do match the two strings which contains brackets.
the perl code is here.
#!/usr/bin/perl -w
$a = "cat(S1)rat";
$b = "cat(S1)r";
if ( $a =~ $b ) {
printf("matching\n");
}
I am not getting the desired output.
please help
snk
There are several answers here, but not a lot address your fundamental misunderstanding.
Here is a simplified version of your problem:
my $str = "tex(t)";
my $pattern = "tex(t)";
if ($str =~ $pattern) {
print "match\n";
} else {
print "NO MATCH\n";
}
This prints out NO MATCH.
The reason for this is the behavior of the =~ operator.
The thing on the left of that operator is treated as a string, and the thing on the right is treated as a pattern (a regular expression).
Parentheses have special meaning in patterns, but not in strings.
For the specific example above, you could fix it with:
my $str = "tex(t)";
my $pattern = "tex\\(t\\)";
More generally, if you want to escape "special characters" in $pattern (such as *, ., etc.), you can use the \Q...\E syntax others have mentioned.
Does it make sense?
Typically, you do not see a pattern represented as a string (as with "tex(t)").
The more common way to write this would be:
if ($str =~ /tex(t)/)
Which could be fixed by writing:
if ($str =~ /tex\(t\)/)
Note that in this case, since you are using a regex object (the /.../ syntax), you do not need to double-escape the backslashes, as we did for the quoted string.
Try this code:
my $p = "cat(S1)rat";
my $q = "cat(S1)r";
if ( index( $p, $q ) == -1 ) {
print "Does not match";
} else {
print "Match";
}
You have to escape the parenthesis:
if ( $a =~ /\Q$b/ ) {
print "matching\n";
}
And please, avoid using variable names $a and $b they are reserved for sorting.
Also, there're no needs to use printf here.

Merge multiple regex, how can I know which part got matched?

Here's a minimal case,
I have multiple regex, that is aa, bb, and cc, in the old days, I just loop through all regex and see if the string can match any of those. If any regex got matched, stop the process.
But now I decided to put it altogether, with a simple OR operation, now I get
(aa)|(bb)|(cc)
So if I get a match, the $1 would be what I wanted, but I wouldn't be able to know if it's (aa) or (bb) or (cc) that did it, any ideas?
In your example, if aa matched, $1 will be set; if bb matched, $1 will be undef and $2 will be set, etc.
if ( defined $1 ) {
print "first part matched: $1.\n";
}
elsif ( defined $2 ) {
print "second part matched: $2.\n";
}
...
or, more dynamically, using #- and #+:
my $string = "xbb";
if ( $string =~ /(aa)|(bb)|(cc)/ ) {
my $match = ( grep defined $-[$_], 1..$#- )[0];
if ( defined $match ) {
print "part $match matched: " . substr( $string, $-[$match], $+[$match]-$-[$match] ) . ".\n";
}
}

Get the output of git show-ref --tags in a hash using Perl

I get the output of
git show-ref --tags, which looks like:
1231243424324234234324242421412414121212 Tagname
1231341253432148224235423652423654782363 tagnametwo
2453491533961372624623247862387746223647 tagnamethree
I want to take these values in a hash, such that the commit id is the key and the tag name is its value.
Since your incoming data is arranged as alternating keys and assoicated values, all you need to do is extract that list and assign it to a hash. The important thing is to get the patterns right.
Defining the Patterns
Given that:
a git id is a sequence of hex digits matched by the pattern \p{ahex}+
its tag is a sequence of non-whitespace matched by the pattern \S+
You could do this to build up a hash mapping ids to tags:
my %id2tag = ();
my $data = `git show-ref --tags`;
while ( $data =~ / (\p{ASCII Hex Digit}+) \s+ (\S+) /gx ) {
$id2tag{ $1 } = $2;
}
Named Groups
If you prefer named groups, support in Perl v5.10 or better, you could do it this way:
use v5.10;
my %id2tag = ();
my $data = `git show-ref --tags`;
while ( $data =~ / (?<ID> \p{ASCII Hex Digit}+ ) \s+ (?<TAG> \S+ ) /gx ) {
$id2tag{ $+{ID} } = $+{TAG};
}
Interpolated Patterns
Or you could store the pattern in variables and interpolate them:
use v5.10;
my %id2tag = ();
my $id_rx = qr/ (?<ID> \p{ASCII Hex Digit} + ) /x;
my $tag_rx = qr/ (?<TAG> \S + ) /x;
my $data = `git show-ref --tags`;
while ( $data =~ / $id_rx \s+ $tag_rx /gx ) {
$id2tag{ $+{ID} } = $+{TAG};
}
That approach is more extensible, and for more complex patterns, more readable.
Hash Assignment
Or you could do it all at once, since everything is already in the right order:
%id2tag = `git show-ref --tags` =~ / (\p{ASCII Hex Digit}+) \s+ (\S+) /gx;
A Real-world Demo
Here is an example of running this on some canned data I extracted from the current git repo for Perl itself.
my $data = do { local $/; <DATA> };
my %id2tag = $data =~ /(\p{ahex}+)\s+(\S+)/gx;
for my $id (sort keys %id2tag) {
print "$id $id2tag{$id}\n";
}
__END__
da71581858ddfe0f74d9e276c5bbe888c75b6d7f refs/tags/GitLive-blead
c4fb1312746aed9e0b696326f3c2664a71284324 refs/tags/GitLive-maint-5.004
5c487ee4c7541fbfd48376965aaefc58ee92541e refs/tags/GitLive-maint-5.005
acb6b050e21c7f3a48affc353ed066777fc1bd0a refs/tags/GitLive-maint-5.10
e03dffa8c006bd53504464a288371e71419497fd refs/tags/GitLive-maint-5.6
9b4fcc40a6ef2b8882b7dda7568ac911f7718b0f refs/tags/GitLive-maint-5.8
790fbd2b1de809897ebacddb828652a3f1d75dd9 refs/tags/code-review/2009-07-22
8d063cd8450e59ea1c611a2f4f5a21059a2804f1 refs/tags/perl-1.0
112e33b1b18999ffceafcdafac4e1888f882dc74 refs/tags/perl-1.0.15
4f78c20b5e222c45328d0f7f30988dc4bbe99c1e refs/tags/perl-1.0.16
378cc40b38293ffc7298c6a7ed3cd740ad79be52 refs/tags/perl-2.0
ffd30a0b488495f48bc676c58309803860e1e715 refs/tags/perl-2.001
a687059cbaf2c6fdccb5e0fae2aee80ec15625a8 refs/tags/perl-3.000
27e2fb84680b9cc1db17238d5bf10b97626f477f refs/tags/perl-3.044
fe14fcc35f78a371a174a1d14256c2f35ae4262b refs/tags/perl-4.0.00
e334a159a5616cab575044bafaf68f75b7bb3a16 refs/tags/perl-4.0.36
a0d0e21ea6ea90a22318550944fe6cb09ae10cda refs/tags/perl-5.000
fec02dd38faf8f83471b031857d89cb76fea1ca0 refs/tags/perl-5.000o
748a93069b3d16374a9859d1456065dd3ae11394 refs/tags/perl-5.001
8e07c86ebc651fe92eb7e3b25f801f57cfb8dd6f refs/tags/perl-5.001n
a5ebd2dff0239f64f342318d01cd92dc19aa52c8 refs/tags/v5.13.9
6a69229c1ab56c42c6097e1a84993663bd6a23f3 refs/tags/v5.14.0
6ac2c00aba1f90aa074905caff75d24ac4224c79 refs/tags/v5.14.0-RC1
a944cabb82c555112be417b6fdcf2abeea9e2c90 refs/tags/v5.14.0-RC2
3a178b6f9ebcc27e659c48b690758c679fec5cc5 refs/tags/v5.14.0-RC3
cf28be8222b700a410ab05a0d7a770e029973b0c refs/tags/v5.14.1
0f687828d8e355bb557d0cf0d3b274cf08f6bae7 refs/tags/v5.14.1-RC1
6a56b9d83500deafe8d850e244906f5513cdd222 refs/tags/v5.14.2
8b560c3170c9c6e2263384868d0287017aecb59d refs/tags/v5.14.2-RC1
cc2b21d47cd66b31fa33901d19112160488dc7d7 refs/tags/v5.15.0
cbeacc71ada41500c888d4cbd36c7314a14843d1 refs/tags/v5.15.1
a818eb37604096e91a51fbb947af5754c7067235 refs/tags/v5.15.2
eaa96143a800c6dca466021db99e8fe857e9e1b1 refs/tags/v5.15.3
5f4091681272a7ec88e3fe7eb014a8083ed95c5f refs/tags/v5.15.4
424c2e57d9f2f65dc08ae9e0d8179f9aa5d581d9 refs/tags/v5.15.5
33a408b7de68c683fad755d341d7899ec1953216 refs/tags/v5.15.6
c605ae044d8a52e820eb785305f0fd4d271faf51 refs/tags/v5.15.7
9b30811fee3478af913382334909acde68afd36d refs/tags/v5.15.8
7b50a15ad488ff4d4f50c99b5e534ede59c2d4af refs/tags/v5.15.9
fb2f5b1704c64fe55da0a05bbc71480c98fe5aac refs/tags/v5.16.0
340c15b2c5103c00d84cd7f15a65ca6ad15a116d refs/tags/v5.16.0-RC1
adee78d52422370b60888092a0155e40cdade038 refs/tags/v5.16.0-RC2
b59ea6d0dab824ba97b3cd1cfc85b3d6e91aab63 refs/tags/v5.16.1
aca8d19feb348b88fde0b018584caa2f40790d69 refs/tags/v5.17.0
002fcd4de39f0355cb9158aca59103d1218836a5 refs/tags/v5.17.1
7ef87a62992c4ed1cad491f65a28ee406fe909e7 refs/tags/v5.17.2
1e37de6eef7be989cb0181b094452e0adbefe976 refs/tags/v5.17.3
Prints out things like:
002fcd4de39f0355cb9158aca59103d1218836a5 refs/tags/v5.17.1
0f687828d8e355bb557d0cf0d3b274cf08f6bae7 refs/tags/v5.14.1-RC1
112e33b1b18999ffceafcdafac4e1888f882dc74 refs/tags/perl-1.0.15
1e37de6eef7be989cb0181b094452e0adbefe976 refs/tags/v5.17.3
27e2fb84680b9cc1db17238d5bf10b97626f477f refs/tags/perl-3.044
33a408b7de68c683fad755d341d7899ec1953216 refs/tags/v5.15.6
340c15b2c5103c00d84cd7f15a65ca6ad15a116d refs/tags/v5.16.0-RC1
378cc40b38293ffc7298c6a7ed3cd740ad79be52 refs/tags/perl-2.0
1) Get the output of the command
my $output = qx/git show-ref --tags/;
2) Write a regular expresion and iterate on it:
my #RESULT;
while( $output =~ m!(\w+)\s*!g ) {
push #RESULT, $1;
}
3) Build your hash from array
my %MY_HASH = #RESULT;

In Perl, I need to read a .conf file that contains condition like $a>$b based on the result of the condition i have to execute either if or else block

The content of condition.conf:
condition1=$a>$b
Example Perl code:
$cnd_ConfFile = $ARGV[0];
open(CONDITIONS, '<', $cndConfFile);
$cndCount=0;
while ( <CONDITIONS> ) {
chomp; # no newline
s/#.*//; # no comments
s/^\s+//; # no leading white
s/\s+$//; # no trailing white
next unless length;
($var, $value) = split(/\s*=\s*/, $_, 2);
$cndOnCounterValues[$cndCount++]=$value;
}
close CONDITIONS;
$cond = $cndOnCounterValues[0];
print "\n$cond\n";
$a=3;
$b=5;
if($cond){
print "a is greater then b";
}
else
{
print "b is greater then a";
}
The above code always gives the output "a is greater then b".
Regardless of the values of $a and $b.
I assume that you want to eval the $a>$b expression that literally appears in your config file. To do that replace:
if ($cond) {
with:
if (eval $cond) {
That should to the trick.
Disclaimer: don't do this unless you know what you are doing (see comments).
Here i a quick example that seems to satisfy your problem.
#! /usr/bin/env perl
use strict;
use warnings;
my #cond;
{
while( <> ){
chomp;
next unless length;
next if m' ^ \s* \# 'x;
next unless m' (\w+) \s* = \s* (.*?) \s* $'x;
push #cond, [$1,$2];
}
}
my($a,$b);
$a=3;
$b=5;
for my $elem ( #cond ){
my($name,$cond) = #$elem;
if( eval $cond ){
print "$name is true, because $cond matches "
}else{
print "$name is false, because $cond doesn't match "
}
print '(', eval("qq{$cond}"), ")\n";
}
echo 'condition1=$a>$b
condition2=$a<$b' | perl test.pl
condition1 is false, because $a>$b doesn't match (3>5)
condition2 is true, because $a<$b matches (3<5)