to match a string and an integer in perl - perl

I have a string of numbers, like "4 2 6 7", and a variable i which is an integer. How can I decide if i is included in the string? The code is in perl...

Use this function:
my $string = "4 2 6 7";
my $i = 4;
if ( $string =~ /\b$i\b/ ) {
print "$string contains $i\n";
}

You can use split to create an array from the string "4 2 6 7", and then use grep to search the array.
$ perl -wle 'if ( grep {$_ eq $i} split(" ", "4 2 6 7") ) {print "matched\n";}'
EDIT:
Or you can use '==' instead of 'eq' as the comparison operator to match numbers instead of strings.

For the fun of it, the ~~ smart match operator:
use 5.012;
my $string = "4 2 6 7";
my #test = split /\s+/, $string;
for( 0 .. 9 ) {
say "$_ is contained in $string" if $_ ~~ #test;
}
A good discussion on the power of the smart match operator is found in perlsyn. It can be a little tricky, since it's not an associative operator, and the rules are deeply rooted in DWIMery rather than consistency. But it's very powerful.

Use this regular expression to match the variable i with a word boundary (assuming your string of numbers have a space after each integer):
/\b$i\b/

Here's a version that does not care about the delimeters or formatting of your string. It just extracts sequences of digits and compares them to the search pattern.
I made it into a sub and a functional program, for convenience.
use warnings;
use strict;
my $string = "4 22 6 7";
my $i = shift; # number you want to search for
print "Checking '$string' for: '$i'\n";
print "Result is: ", (is_in($string, $i) ? "Yes" : "No");
sub is_in {
my ($string, $i) = #_;
while ( $string =~ /(\d+)/g ) {
return 1 if ( $1 == $i );
}
return 0;
}
Example output:
C:\perl>t4.pl 4
Checking '4 22 6 7' for: '4'
Result is: Yes
C:\perl>t4.pl 22
checking '4 22 6 7' for: '22'
Result is: Yes
C:\perl>t4.pl 2
checking '4 22 6 7' for: '2'
Result is: No

You can do it easily with the help of split function.
use warnings;
my $string = "4 2 6 7";
my $i = 6; #use any value of $i
my #x = split / /, $string;
my $count = 0;
foreach (#x)
{
if($_ == $i)
{
print "matched at position $count"; die $!;
}
$count++;
}
print "integer doesn't found in string";
Try it on codepad: http://codepad.org/f5a86c9s

Related

Splitting and tallying substrings within mixed integer-string data

Input Data (example):
40A3B35A3C
30A5B28A2C2B
Desired output (per-line) is a single number determined by the composition of the code 40A3B35A3C and the following rules:
if A - add the proceeding number to the running total
if B - add the proceeding number to the running total
if C - subtract the proceeding number from the running total
40A 3B 35A 3C would thus produce 40 + 3 + 35 - 3 = 75.
Output from both lines:
75
63
Is there an efficient way to achieve this for a particular column (such as $F[2]) in a tab-delimited .txt file using a one-liner? I have considered splitting the entire code into individual characters, then performing if statement checks to detect A/B/C, but my Perl knowledge is limited and I am unsure how to go about this.
When you use split with a capture, the captured group is returned from split, too.
perl -lane '
#ar = split /([ABC])/, $F[2];
$s = 0;
$s += $n * ("C" eq $op ? -1 : 1) while ($n, $op) = splice #ar, 0, 2;
print $s
' < input
Or maybe more declarative:
BEGIN { %one = ( A => 1,
B => 1,
C => -1 ) }
#ar = split /([ABC])/, $F[2];
$s = 0;
$s += $n * $one{$op} while ($n, $op) = splice #ar, 0, 2;
print $s
When working through a string like this, it's useful to know that regular expressions can return a list of results.
E.g.
my #matches = $str =~ m/(\d+[A-C])/g; #will catch repeated instances
So you can do something like this:
#!/usr/bin/env perl
use strict;
use warnings;
while (<DATA>) {
my $total;
#break the string into digit+letter groups.
for (m/(\d+[A-C])/g) {
#separate out this group into num and code.
my ( $num, $code ) = m/(\d+)([A-C])/;
print "\t",$num, " => ", $code, "\n";
if ( $code eq "C" ) {
$total -= $num;
}
else {
$total += $num;
}
}
print $total, " => ", $_;
}
__DATA__
40A3B35A3C
30A5B28A2C2B
perl -lne 'push #a,/([\d]+)[AB]/g;
push #b,/([\d]+)[C]/g;
$sum+=$_ for(#a);$sum-=$_ for(#b);
print $sum;#a=#b=();undef $sum' Your_file
how it works
use the command line arg as the input
set the hash "%op" to the
operations per letter
substitute the letters for operators in the
input evaluate the substituted input as an expression
use strict;
use warnings;
my %op=qw(A + B + C -);
$ARGV[0] =~ s/(\d+)(A|B|C)/$op{$2} $1/g;
print eval($ARGV[0]);

Why we can not use "le" in for loop and can use in "if condition"

I've made this program as a beginner. Need to clarify something!
Why I m not able to use "le" in a for loop given below but I'm able to use it in "if condition". What is the reason behind that. ?
print "Type the number upto series should run\n";
my $var;
$var = int<STDIN>;
chomp $var;
my ($i, $t);
my $x = 0;
my $y = 1;
if($var eq 1) {
print "\nYour Series is : $x\n";
} elsif($var eq 2){
print "\nYour Series is : $x $y\n";
} elsif($var ge 2) {
print "Your Series is : $x $y ";
for($i = 1; $i le $var - 2; $i++) {
# Why the condition not working when I'm using "le"
# but it does work when I'm using "<="
$t = $x + $y;
$x = $y;
$y = $t;
print "$t ";
}
print "\n";
} else {
print "Error: Enter a valid postive integer\n";
}
You are free to use le and <= as you like. But you should be aware that they are completely different operators.
Numeric comparision operators are
== != < <= > >= <=>
The equivalent string comparision operators are
eq ne lt le gt ge cmp
Strings and numbers are converted to each other as needed. This means for example that
3 ge 20 # true: 3 is string-greater than 20
11 le 2 # true: 11 is string-less-or-equal than 2
because lexicographic ordering compares character by character. Using the numeric operators when you want to treat the contents of your $variables as numbers is therefore preferable and will yield the correct results.
Note that Perl translates between strings and numbers invisibly. It is advisable to use warnings, so that you get a helpful message when a string can't represent a number (e.g. "a").
The correct answer has been given, that ge does string comparison, where for example 11 is considered less than 2. The solution is to use numerical comparison, == and >=.
I thought I would help demonstrate the problem you are having. Consider this demonstration of how the default sort works:
$ perl -le 'print for sort 1 .. 10'
1
10
2
3
4
5
6
7
8
9
As you can see, it considers 10 lower than 2, and that is because of string comparison, which is the default mode for sort. This is how the default sorting routine looks under the hood:
sort { $a cmp $b }
cmp belongs to the string comparison operators, like eq, le, and ge. These operators are described here (cmp is below that).
For the sort routine to do what we expect in the above example, we would have to use a numeric comparison operator, which is the "spaceship operator" <=>:
sort { $a <=> $b }
In your case, you can try out your problem with this one-liner:
$ perl -nlwe 'print $_ ge 2 ? "Greater or equal" : "Lesser"'
1
Lesser
2
Greater or equal
3
Greater or equal
11
Lesser
When you compare numbers with eq, le, gt..... etc; they first be converted to string. And strings will be checkd for alphabatical order, so "11" will be less then "2" here.
So you should be using ==,<=,>= ...etc when comparing numbers.
I thought you may like to see a more Perl-like program that produces the Fibonnaci series like yours.
use strict;
use warnings;
print "Type the length of the series\n";
chomp(my $length = <>);
unless ($length and $length !~ /\D/ and $length > 0) {
print "Error: Enter a positive integer\n";
}
print "\n";
my #series = (0, 1);
while (#series < $length) {
push #series, $series[-2] + $series[-1];
}
print "#series[0..$length-1]\n";

extracting numbers from strings in perl

$string1 = "2_0_1AU13682.0AV+0.2"
$string2 = "2_0_1AT+0.1CD13681.9"
I have these 2 strings. How do I extract the 7 char decimal number from both of them?
From string 1 that would be 13682.0 and from string 2 that would be 13681.9.
The decimal number is always 7 characters and always in the form of xxxxx.x
Try doing this using regex and match operator :
my $string1 = "2_0_1AU13682.0AV+0.2";
my ($res) = $string1 =~ m/(\d{5}\.\d)/;
print $res, "\n";
See Extracting-matches and Regexp-Quote-Like-Operators.
Use a regular expression, like this:
my #data = qw( 2_0_1AU13682.0AV+0.2 2_0_1AT+0.1CD13681.9 );
foreach my $str (#data) {
if ($str =~ /(\d{5}\.\d)/) {
print $1, "\n";
}
}
Outputs:
13682.0
13681.9

Possible combinations made?

In Perl, how can I test for all possible combinations in a number.
For example, the combination I am interested in is separating.
E.g: 53 could be "5 3" or just "53"
E.g: 215 could be "21 5" or "2 15"
In fact, you are distributing spaces to all the positions between characters. On each position, the space either is realized or not for each combination. Therefore, you can represent it as a binary number, 1 means space present, 0 means space not present.
#!/usr/bin/perl
use warnings;
use strict;
my $num = shift;
my #digits = split //, $num;
my $length = length($num) - 1;
if ($length == 0) {
print "$num\n";
exit;
}
for my $i (0 .. 2 ** $length - 1) {
my $mask = sprintf "%0${length}b", $i;
my #replace_arr = split //, $mask;
my $idx = 0;
for (#replace_arr, '') {
print $digits[$idx];
print ' ' if $_;
$idx++;
}
print "\n";
}

Perl multidimensional array question

I have a program that prints out the location of commas in a paragraph of text in the form
For example if the paragraph is
one,two,three
three and a half
four,five
six
seven,eight
The program will print
0:4
0:8
2:5
4:6
I would like to use this output to create an array where the numbers after the colon are listed across columns in the row specified by the index before the colon. The array formed by the coordinates above would be
4 8
<blank or character '.'>
5
<blank or character '.'>
6
so array[0,0] = 4, array[0,1] = 8
array[1,0] = empty
array[2,0] = 5
etc...
I bet this is simple but I need help to write it.
$data_file="file.out";
open(DAT, $data_file) || die("Could not open file!");
#raw_data=<DAT>;
close(DAT);
my %array;
my $line = 0;
foreach $newline(#raw_data) {
chomp;
while ( $newline=~m/(,|;|:|and)/) {
push #{ $array{$line} }, pos($newline); # autovivification
}
$line++; }
Program
#!/usr/bin/env perl
use strict;
use warnings;
my %array;
my $line = 0;
while (<DATA>) {
chomp;
while ( /(,|;|:|(?:and))/g ) {
my $position = pos() - length($1) + 1;
push #{ $array{$line} }, $position; # autovivification
}
$line++;
}
for ( sort { $a <=> $b } keys %array ) {
my $flattened_value = join ', ', #{ $array{$_} };
print "$_ -> $flattened_value\n";
}
__DATA__
one,two,three
three and a half
four,five
six
seven,eight
Output
0 -> 4, 8
1 -> 7
2 -> 5
4 -> 6
Refer: chomp, join, keys, sort, split.
Refer the following documents to get an understanding of Perl's data structures and especially autovivification which has been used in this example.
perldoc perlref
perldoc perlreftut