How to produce range with step in Perl - perl

In Bash, seq 5 5 20 produces 5 10 15 20.
In Perl, 1..5 produces 1 2 3 4 5; does it support step?
How do I produce a range with step in Perl?

perldoc -f map is one way:
use warnings;
use strict;
use Data::Dumper;
my #ns = map { 5 * $_ } 1 .. 4;
print Dumper(\#ns);
__END__
$VAR1 = [
5,
10,
15,
20
];
See also: perldoc perlop

The range operator in Perl doesn't support steps. You could use a for loop instead:
for (my $i = 5; $i <= 20; $i += 5) {
print "$i\n";
}

The List::Gen range function does this:
use strict;
use warnings;
use feature 'say';
use List::Gen;
my $range = range 5, 20, 5;
say for #$range; # 5
# 10
# 15
# 20
say while <$range>; # TIMT1WTDI
$range->say; # TAMT2WTDI, v.0.974
say $range->str; # TAMT3WTDI, v.0.974
my $by_fives = <5 .. 20 by 5>;
say while <$by_fives>; #TAMT4WTDI
<5 .. * by 5>->say( 4 ); #TAMT5WTDI

Not as good as toolic's answer:
use warnings;
use strict;
my #ns;
for my $n (1..4) {
push(#ns, $n*5);
}

I wrote Acme::Range::Module a while back as a gag module - hence the Acme:: namespace - but it does do what you want and has tests and is supported. Here's the example code:
use Acme::Globule qw( Range );
foreach (<10..1>) {
print "$_... ";
}
print "Lift-off!\n";
# put down that crack pipe...
sub my_keys(\%) {
my #hash = %{ $_[0] };
return #hash[ glob("0,2..$#hash") ];
}
sub my_values(\%) {
my #hash = %{ $_[0] };
return #hash[ glob("1,3..$#hash") ];
}

Here is an easy solution that utilizes map and the built-in range operator:
sub range {
my ($start, $end, $step) = #_;
$step ||= 1;
return map { $_ * $step } ($start / $step .. $end / $step);
}
Notice the key point here is the map {} block. We simply divide the end
by the given step (works for negative and positive) then map each value
to the multiple of the given step.

Like the map solution :)
Here it is used to look for files starting with even numbers in a range 116 to 648:
perl -e 'foreach (map { 2 * $_ } (116/2) .. (648/2)) { system("ls -l $_*"); } '
Perl is just wonderful for some jobs and making funny one-liners :)

Related

In Perl, how can I loop through variable names? [duplicate]

This question already has answers here:
How can I use a variable as a variable name in Perl?
(3 answers)
Closed 8 years ago.
I have N array and i want to print 1st elements of them in single for loop
my code:
#arr1=qw(1..5);
#arr2=qw(6..10);
...
#arrN=qw(...);
for($i=1;$i<=$N;$i++)
{
print "$arr[$i][0]";
}
When you find that you need to know the names of 0 .. N different variables. Its time to consider that you might be doing it wrong.
Arrays = list of 0 .. N values, can be sequential
Hash = list of 0 .. N named values
For your arrays, unless you actually want to be converting to strings, don't use qw() just use the bare ()
See solution below, you need an array of arrays:
#!/usr/bin/perl
use strict;
use warnings;
my $n = 10;
my #nArrays;
#fills the nArrays list with array_refs
for my $num(0 .. $n){
push #nArrays, [($num .. $num+5)];
}
#Print out the arrays, one per row, long way
for my $array (#nArrays){
print $array->[0], "\n";
}
If, contrary to all normal recommendations, you leave use strict; out of your script, you could use:
$N = 3;
#arr1 = (1..5);
#arr2 = (6..10);
#arr3 = (7..12);
#arrs = ("arr1", "arr2", "arr3");
for ($i = 0; $i < $N; $i++)
{
print "$arrs[$i][0]\n";
}
Output:
1
6
7
This is absolutely not recommended, but it does work still (mainly for reasons of backwards compatibility).
With use strict; etc, you can use explicit references:
use strict;
use warnings;
my $N = 3;
my #arr1 = (1..5);
my #arr2 = (6..10);
my #arr3 = (7..12);
my #arrs = (\#arr1, \#arr2, \#arr3);
for (my $i = 0; $i < $N; $i++)
{
print "$arrs[$i][0]\n";
}
Output:
1
6
7
On the whole, though, you would do better with a single array of arrays, perhaps like this:
use strict;
use warnings;
my $N = 3;
my #arr = ( [1..5], [6..10], [7..12] );
for (my $i = 0; $i < $N; $i++)
{
print "$arr[$i][0]\n";
}
Output:
1
6
7
What you have in mind is called a symbolic reference, and generally not a good idea.
If the values of these variables belong together, there is already a data structure that is indexed by an integer: Use an array to hold them:
use strict;
use warnings;
my #arr = (
[ 1 .. 5 ],
[ 6 .. 10 ],
# ...
[ 1_000_000 .. 1_000_005 ],
);
for my $i (0 .. $#arr) {
print $arr[$i][0], "\n";
}

Perl String::Approx for values that taken from MySQL DataBase

When I apply String::Approx to the values taken from the database, it doesn't check the matching words.
The program is as follows...
use String::Approx qw (amatch);
my #matches = qw();
my %matchhash = qw();
my #x=$search_keyword;
my $i=0;
my $j=0;
my $l=1;
my $qry1="SELECT * FROM AuctionCategoriesName";
my $prp1 = $dbh1->prepare($qry1);
$prp1->execute();
while(my $row1=$prp1->fetchrow_hashref())
{
$y=$row1->{'Name'};
#name="(['$y'])";
#match = grep { amatch (#x, #$_) } #name;
$cnt=$#match;
if($cnt < 1)
{
$matches[$i]=$match[0];
$i++;
}
}
Connections to db are perfect.
I want to get the approximately matched names from db amoung all the values present in the $row1->{'Name'}. The values must be kept stored in $matches[$i]. I call those values as:
foreach my $k (#matches) { print "$k <br/>" }
As Richard said this line is unnecessary :-
#name="(['$y'])";
You could write $y as is and perl does the right thing.
The grep followed by the if is duplication of the same work. amatch returns true/false in scalar context so we can use that.
You could simplify your code along these lines :-
1 use String::Approx qw (amatch);
2 use strict;
3 my #matches = qw();
4 my $search_keyword = 'Hello';
5 foreach my $y ('hello', 'world')
6 {
7 if (amatch ($search_keyword, ($y)))
8 {
9 push #matches, $y;
10 }
11 }
12
13 print #matches
The output is :-
$ perl test.pl
hello

Create vectors with different lengths in perl

I have wrote the following program:
use strict;
use warnings;
use 5.010;
my $nodesNumber = 100 ;
my $communitiesNumber = 10;
my $prob_communities = 0.3;
for my $i (1 .. $nodesNumber){
for my $j (1 .. $communitiesNumber){
my $random_number=rand();
if ($prob_comunities > $random_number){
say "$i $j";
}
}
}
This program gives as output a list of two columns of integers as:
1 2
1 4
2 2
2 5
2 7
...
I would like to create a vector in which the first element in the left column is counted once and the right column elements represents the value of the vector's components. I would like the output to look like:
vector[0][0]= 1
vector[0][1]= 2
vector[0][2]= 4
vector[1][0]= 2
vector[1][1]= 2
vector[1][2]= 5
vector[1][3]= 7
Any help?
#!/usr/bin/env perl
# file: build_vector.pl
use strict;
use warnings;
my #vector; # the 2-d vector
my %mark; # mark the occurrence of the number in the first column
my $index = -1; # first dimensional index of the vector
while (<>) {
chomp;
my ($first, $second) = split /\s+/;
next if $second eq '';
if (not exists $mark{$first}) {
$mark{ $first } = ++$index;
push #{ $vector[$index] }, $first;
}
push #{ $vector[$index] }, $second;
}
# dump results
for my $i (0..$#vector) {
for my $j (0..$#{ $vector[$i] }) {
print "$vector[$i][$j] ";
}
print "\n";
}
This script will processing the output of your script and build the vector in #vector. If your script has filename generator.pl, you can call:
$ perl generator.pl | perl build_vector.pl
UPDATE:
use strict;
use warnings;
my $nodesNumber = 100 ;
my $communitiesNumber = 10;
my $prob_communities = 0.3;
my #vector; # the 2-d vector
my %mark; # mark the occurrence of the number in the first column
my $index = -1; # first dimensional index of the vector
for my $i (1 .. $nodesNumber){
for my $j (1 .. $communitiesNumber){
my $random_number=rand();
if ($prob_communities > $random_number){
if (not exists $mark{$i}) {
$mark{ $i } = ++$index;
push #{ $vector[$index] }, $i;
}
push #{ $vector[$index] }, $j;
}
}
}
# dump results
for my $i (0..$#vector) {
for my $j (0..$#{ $vector[$i] }) {
print "$vector[$i][$j] ";
}
print "\n";
}
#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
use Const::Fast;
use Math::Random::MT;
const my $MAX_RAND => 10;
my $rng = Math::Random::MT->new;
my #v = map {
my $l = $rng->irand;
[ map 1 + int($rng->rand($MAX_RAND)), 0 .. int($l) ];
} 1 .. 5;
use YAML;
print Dump \#v;

$array can't print anything

This is my program , I want to let user type a matrix line by line and print the while matrix , but I can't see the matrix
The user will type
1 2 3
4 5 6
7 8 9
like this
and I want to let it show
1 2 3
4 5 6
7 8 9
Perl program
$Num = 3;
while($Num > 0 )
{
$Row = <STDIN>;
$Row = chomp($Row);
#Row_array = split(" ",$Row);
push #P_matrix , #Row_array;
#Row_array = ();
$Num = $Num - 1;
}
for($i=0;$i<scalar(#P_matrix);$i++)
{
for($j=0;$j<scalar(#P_matrix[$i]);$j++)
{
printf "$d ",$P_matrix[$i][$j];
}
print "\n";
}
I change the expression => printf "$d ",$P_matrix[$i][$j]; to print $P_matrix[$i][$j]
but still don't work.
To create a multi-dimensional array, you have to use references. Use
push #P_matrix, [ #Row_array ];
to create the desired structure.
Also, chomp does not return the modified string. Simply use
chomp $Row;
to remove a newline from $Row. Moreover, chomp is not needed at all if you split on ' '.
printf uses % as the formatting character, not $.
You can use Data::Dumper to inspect complex data structures. Use strict and warnings to help you avoid common problems. Here is how I would write your program:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my #p_matrix;
push #p_matrix , [ split ' ' ] while <>;
warn Dumper \#p_matrix;
for my $i (0 .. $#p_matrix)
{
for my $j (0 .. $#{ $p_matrix[$i] })
{
printf '%d ', $p_matrix[$i][$j];
}
print "\n";
}
First and foremost please use use strict; use warnings;
Issues in your code:
You have a single dimensional array, but your are trying to access
it like two dimensional array. In order to make 2 dimensional array push the array reference of Row_array in #P_matrix as [#Row_array].
Where is $d defined? declare $d as my $d or our $d if you mean $d as scalar variable.
OR
For using %d, use need sprintf. Please read this.

How can I improve this commify routine for speed?

I need an efficient commify filter or routine for use with Template::Toolkit. It is to be used many times on the page. It should support decimals.
This one is found in The Perl Cookbook:
sub commify {
my $text = reverse $_[0];
$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
return scalar reverse $text;
}
Are there more efficient ways?
Before you try to optimize anything, be sure its actually a problem. Use a profiler to find the problem areas in your code and focus on those areas.
That commify solution is about as good as you can get, but there are other things you might do if you need to bypass it:
Use something like Memoize to cache results if you are commifying the same numbers repeatedly
Pre-compute all the numbers if they are unlikely to change.
Cache processed templates when you can
Use a reverse proxy setup with your webserver to hand off heavy processing to backend servers.
I think that if you are worried about speed of this - you are seriously misplacing your worries.
The commify function works on my desktop 130000 times per second on number like: "31243245356.4432".
this means that if you have on your page 10000 numbers, commification of it will take 76ms. And template toolkit processing of the page will probably take 2-3 times as long.
Another option is:
sub commify {
my $text = shift;
1 while $text =~ s/ ( \d ) ( \d{3} ) (\D|\z) /$1,$2$3/xms;
return $text;
}
When it comes to deciding which is faster, the Benchmark module is very useful.
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark;
sub your_commify {
my $text = reverse 100000000;
$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
return scalar reverse $text;
}
sub my_commify {
my $text = 100000000;
1 while $text =~ s/ ( \d ) ( \d{3} ) (\D|\z) /$1,$2$3/xms;
return $text;
}
timethese(
-10,
{
'yours' => \&your_commify,
'mine' => \&my_commify,
}
);
Runing this gives:
~$ ./benchmark.pl
Benchmark: running mine, yours for at least 10 CPU seconds...
mine: 10 wallclock secs (10.01 usr + 0.01 sys = 10.02 CPU) # 111456.89/s (n=1116798)
yours: 11 wallclock secs (10.04 usr + 0.00 sys = 10.04 CPU) # 250092.33/s (n=2510927)
Looks like yours is ~2.25 times faster! (When using the "at least 10 CPU seconds" mode you have to check the values of "n" used.)
So it looks like you have to keep searching... but remember to use Benchmark!
sub commify {
my $n = $_[0];
my $s = abs($n) != $n;
my $x = index($n, '.');
$x = length($n) if $x == -1;
substr($n, $x, 0, ',') while ($x -= 3) > $s;
return $n;
}
I agree with brian and depesz: from a practical point of view, this function is probably not the place to start if you're trying to improve the performance of your app. That said, it is possible to write a much faster commify function. One way is to avoid regular expressions.
use strict;
use warnings;
use Benchmark qw(cmpthese);
sub commify_regex {
my $text = reverse $_[0];
$text =~ s{(\d\d\d)(?=\d)(?!\d*\.)}{$1,}g;
return scalar reverse $text;
}
sub commify_substr {
my $v = $_[0];
my $len = length $v;
my $dec = index($v, '.');
my $i = 3 + ($dec < 0 ? 0 : $len - $dec);
$len -- unless $v == abs($v);
while ($i < $len ++){
substr($v, -$i, 0, ',');
$i += 4;
}
return $v;
}
my #tests = qw(
1 12 123 1234 12345 123456 1234567
12345678 123456789 1234567890 12345678901
123456789012 1234567890123
);
push #tests, map "$_.$_", #tests;
push #tests, map - $_, #tests;
for my $t (#tests){
print "Incorrect for: ", $t, "\n"
unless commify_substr($t) eq commify_regex($t);
}
cmpthese( -2, {
regex => sub { commify_regex($_) for #tests },
substr => sub { commify_substr($_) for #tests },
});
# Output on my Windows machine.
Rate regex substr
regex 3277/s -- -54%
substr 7109/s 117% --
This can be done using single regexp:
$number =~ s/(?<=\d)(?=(?:\d\d\d)+(?!\d))/,/g;