I am trying to use List::MoreUtils methods. But, need some clarity on its usage it in some scenarios.
Please let me know, if it can be used with a map. For example:
#!/usr/bin/perl
use strict;
use warnings;
use List::Util;
use List::MoreUtils;
use Data::Dumper;
my #udData1 = qw(WILL SMITH TOMMY LEE JONES);
my #arr = qw(WILL TOMMY);
my %output = map{$_=>List::MoreUtils::firstidx{/$_/} #udData1} #arr;
print Dumper %output;
print List::MoreUtils::firstidx{/TOMMY/} #udData1;
print "\n";
Output:
$VAR1 = 'TOMMY';
$VAR2 = 0;
$VAR3 = 'WILL';
$VAR4 = 0;
2
As observed I am not getting the values correctly when using map, but getting it fine when used in the later command.
I intend to use $_ as an element of #arr. This may be incorrect. So, please suggest me an alternative. Shall i have to use foreach?
The problem is this bit right here:
List::MoreUtils::firstidx{/$_/} #udData1
In this bit of code, you're expecting $_ to be both the pattern taken from #arr and the string taken from #udData1 at the same time. (Remember that firstidx{/TOMMY/} means firstidx{$_ =~ /TOMMY/}, and likewise firstidx{/$_/} means firstidx{$_ =~ /$_/}.)
What actually happens is that $_ is the value from #udData1 (since that's the innermost loop) and you wind up matching that against itself. Because it's a simple alphabetic string, it always matches itself, and firstidx correctly returns 0.
Here's one solution using a temporary lexical variable:
my %output = map{ my $p = $_;
$p => List::MoreUtils::firstidx{/$p/} #udData1 } #arr;
Related
I was wondering if it is possible to make a hash assigning its keys and values at once. Or in general use map and for at one line:
#!/usr/bin/perl
%h = map {$_, $i} qw[a b c] for $i (1..3)
But unfortunatelly not => Number found where operator expected, meant number in the parenthesis. So my question is why am I not able to make double loop by this way? And how otherwise would someone assign hash keys to values (and I dont concern something like $h = {a=>1,b=>2,c=>3} but rather assigning %h = (#keys = #values) ... in other words, how to assign hash by:
2 arrays only (#keys,#values), no scalars
At once (at one line - without block)
Is it even possible in perl?
Populating a hash is simply a matter of assigning a list with alternating keys and values, so you just have to construct the list using the two arrays in an alternating fashion.
use strict;
use warnings;
my #keys = qw(a b c);
my #values = 1..3;
my %h = map { ($keys[$_], $values[$_]) } 0..$#keys;
List::UtilsBy provides a useful abstraction for this in zip_by.
use List::UtilsBy 'zip_by';
my %h = zip_by { #_ } \#keys, \#values;
But actually it's even easier to use slice assignment. Though you technically can't do this in the same statement as the declaration, it's by far the neatest option:
my %h;
#h{#keys} = #values;
Use List::MoreUtils 'zip' or add your own since that module is not a core module:
sub zip(\##){map{($_[0][$_-1],$_[$_])}1..#{$_[0]}}
my %h = zip #keys, #values;
Well, the question is not very clear on 'why?' -- same can be achieved with following code
use strict;
use warnings;
use Data::Dumper;
my $debug = 1;
my %h;
#h{qw(a b c)} = (1..3);
print Dumper(\%h) if $debug;
I am learning Perl for work and I'm trying to practise with some basic programs.
I want my program to take a string from STDIN and modify it by taking the last character and putting it at the start of the string.
I get an error when I use variable $str in $str = <STDIN>.
Here is my code:
my $str = "\0";
$str = <STDIN>;
sub last_to_first {
chomp($str);
pop($str);
print $str;
}
last_to_first;
Exec :
Matrix :hi
Not an ARRAY reference at matrix.pl line 13, <STDIN> line 1.
Why your approach doesn't work
The pop keyword does not work on strings. Strings in Perl are not automatically cast to character arrays, and those array keywords only work on arrays.
The error message is Not an ARRAY reference because pop sees a scalar variable. References are scalars in Perl (the scalar here is something like a reference to the address of the actual array in memory). The pop built-in takes array references in Perl versions between 5.14 and 5.22. It was experimental, but got removed in the (currently latest) 5.24.
Starting with Perl 5.14, an experimental feature allowed pop to take a scalar expression. This experiment has been deemed unsuccessful, and was removed as of Perl 5.24.
How to make it work
You have to split and join your string first.
my $str = 'foo';
# turn it into an array
my #chars = split //, $str;
# remove the last char and put it at the front
unshift #chars, pop #chars;
# turn it back into a string
$str = join '', #chars;
print $str;
That will give you ofo.
Now to use that as a sub, you should pass a parameter. Otherwise you do not need a subroutine.
sub last_to_first {
my $str = shift;
my #chars = split //, $str;
unshift #chars, pop #chars;
$str = join '', #chars;
return $str;
}
You can call that sub with any string argument. You should do the chomp to remove the trailing newline from STDIN outside of the sub, because it is not needed for switching the chars. Always build your subs in the smallest possible unit to make it easy to debug them. One piece of code should do exactly one functionality.
You also do not need to initialize a string with \0. In fact, that doesn't make sense.
Here's a full program.
use strict;
use warnings 'all';
my $str = <STDIN>;
chomp $str;
print last_to_first($str);
sub last_to_first {
my $str = shift;
my #chars = split //, $str;
unshift #chars, pop #chars;
$str = join '', #chars;
return $str;
}
Testing your program
Because you now have one unit in your last_to_first function, you can easily implement a unit test. Perl brings Test::Simple and Test::More (and other tools) for that purpose. Because this is simple, we'll go with Test::Simple.
You load it, tell it how many tests you are going to do, and then use the ok function. Ideally you would put the stuff you want to test into its own module, but for simplicity I'll have it all in the same program.
use strict;
use warnings 'all';
use Test::Simple tests => 3;
ok last_to_first('foo', 'ofo');
ok last_to_first('123', '321');
ok last_to_first('qqqqqq', 'qqqqqq');
sub last_to_first {
my $str = shift;
my #chars = split //, $str;
unshift #chars, pop #chars;
$str = join '', #chars;
return $str;
}
This will output the following:
1..3
ok 1
ok 2
ok 3
Run it with prove instead of perl to get a bit more comprehensive output.
Refactoring it
Now let's change the implementation of last_to_first to use a regular expression substitution with s/// instead of the array approach.
sub last_to_first {
my $str = shift;
$str =~ s/^(.+)(.)$/$2$1/;
return $str;
}
This code uses a pattern match with two groups (). The first one has a lot of chars after the beginning of the string ^, and the second one has exactly one char, after which the string ends $. You can check it out here. Those groups end up in $1 and $2, and all we need to do is switch them around.
If you replace your function in the program with the test, and then run it, the output will be the same. You have just refactored one of the units in your program.
You can also try the substr approach from zdim's answer with this test, and you will see that the tests still pass.
The core function pop takes an array, and removes and returns its last element.
To manipulate characters in a string you can use substr, for example
use warnings;
use strict;
my $str = <STDIN>;
chomp($str);
my $last_char = substr $str, -1, 1, '';
my $new_str = $last_char . $str;
The arguments to substr mean: search the variable $str, at offset -1 (one from the back), for a substring of length 1, and replace that with an empty string '' (thus removing it). The substring that is found, here the last character, is returned. See the documentation page linked above.
In the last line the returned character is concatenated with the remaining string, using the . operator.
You can browse the list of functions broken down by categories at Perl functions by category.
Perl documentation has a lot of goodies, please look around.
Strings are very often manipulated using regular expressions. See the tutorial perlretut, the quick start perlrequick, the quick reference perlreref, and the full reference perlre.
You can also split a string into a character array and work with that. This is shown in detail in the answer by simbabque, which packs a whole lot more of good advice.
This is for substring function used for array variables:
my #arrays = qw(jan feb mar);
last_to_first(#arrays);
sub last_to_first
{
my #lists = #_;
my $last = pop(#lists);
#print $last;
unshift #lists, $last;
print #lists;
}
This is for substring function used for scalar variables:
my $str = "";
$str = <STDIN>;
chomp ($str);
last_to_first($str);
sub last_to_first
{
my $chr = shift;
my $lastchar = substr($chr, -1);
print $lastchar;
}
Basically i am trying to access the predefined variable in a perl program.
the variables are in the form a1 a2 a3 format.
I want to access them in a loop. In the loop I will increment postfix scalar value
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
WHAT I EXPECT:
When I print $var in loop, I need the values 10,12 .. defined earlier.
WHAT I CAN NOT DO:
I am aware that such situation can be handled with a hash. But I do not have any control over the variable naming, hence I can not use hash or change variable format.
I appreciate your help!
If you want to avoid turning off strict, you could use eval:
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
print eval "\$a$i";
}
Update: using more readable version suggested by Сухой27 in the comments
Use an array instead of multiple similarly named variables, as this is their main use case,
use strict;
use warnings;
my #a = (10,12,14,16);
for my $i (0 .. $#a) {
my $var = $a[$i];
print $var, "\n";
}
alternatively you can use array of scalar references
use strict;
use warnings;
my ($a0,$a1,$a2,$a3) = (10,12,14,16);
my #a = \($a0,$a1,$a2,$a3);
for my $i (0 .. $#a) {
my $var = ${ $a[$i] };
print $var, "\n";
}
What you are doing here is called a symbolic reference, and it is an EXTREMELY bad idea.
Please take a look through this article: http://perl.plover.com/varvarname.html
But the long and short of it is - using a variable as a variable name - which you're doing - is dangerous and unnecessary. It causes all sorts of potential problems in your code, including bugs in completely unrelated pieces of code. This is why strict won't let you do it.
More importantly - it's completely unnecessary, because perl has the hash as a native data type.
Instead of your code, consider instead:
my %h;
( $h{0}, $h{1}, $h{2}, $h{3} ) = ( 10, 12, 14, 16 );
foreach my $key ( sort keys %h ) {
print "$key = $h{$key}\n";
}
Now, it's added a few characters to your code, but by doing so - you've created a lexically scoped namespace called %h. (I'd suggest calling it something more meaningful, personally - and definitely avoid $a and $b because they have special meanings).
But there is no danger of this namespace trampling over other parts of your code, and for bonus points - you no longer need your 'for' loop, you can simply iterate on keys instead. (So you always have the right number).
(Or as another user has suggested - just use an array)
You can get round strict's restriction on dynamic variable names like this.
#!/usr/bin/perl
use strict;
use warnings;
{
no strict 'refs';
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
}
I don't think this is a good idea, though!
Consider the following Perl code.
#!/usr/bin/perl
use strict;
use warnings;
$b="1";
my $a="${b}";
$b="2";
print $a;
The script obviously outputs 1. I would like it to be whatever the current value of $b is.
What would be the smartest way in Perl to achieve lazy evaluation like this? I would like the ${b} to remain "unreplaced" until $a is needed.
I'm more interested in knowing why you want to do this. You could use a variety of approaches depending on what you really need to do.
You could wrap up the code in a coderef, and only evaluate it when you need it:
use strict; use warnings;
my $b = '1';
my $a = sub { $b };
$b = '2';
print $a->();
A variant of this would be to use a named function as a closure (this is probably the best approach, in the larger context of your calling code):
my $b = '1';
sub print_b
{
print $b;
}
$b = '2';
print_b();
You could use a reference to the original variable, and dereference it as needed:
my $b = '1';
my $a = \$b;
$b = '2';
print $$a;
What you want is not lazy evaluation, but late binding. To get it in Perl, you need to use eval.
my $number = 3;
my $val = "";
my $x = '$val="${number}"';
$number = 42;
eval $x;
print "val is now $val\n";
Be advised that eval is usually inefficient as well as methodically atrocious. You are almost certainly better off using a solution from one of the other answers.
Perl will interpolate a string when the code runs, and i don't know of a way to make it not do so, short of formats (which are ugly IMO). What you could do, though, is change "when the code runs" to something more convenient, by wrapping the string in a sub and calling it when you need the string interpolated...
$b = "1";
my $a = sub { "\$b is $b" };
$b = "2";
print &$a;
Or, you could do some eval magic, but it's a bit more intrusive (you'd need to do some manipulation of the string in order to achieve it).
As others have mentioned, Perl will only evaluate strings as you have written them using eval to invoke the compiler at runtime. You could use references as pointed out in some other answers, but that changes the way the code looks ($$a vs $a). However, this being Perl, there is a way to hide advanced functionality behind a simple variable, by using tie.
{package Lazy;
sub TIESCALAR {bless \$_[1]} # store a reference to $b
sub FETCH {${$_[0]}} # dereference $b
sub STORE {${$_[0]} = $_[1]} # dereference $b and assign to it
sub new {tie $_[1] => $_[0], $_[2]} # syntactic sugar
}
my $b = 1;
Lazy->new( my $a => $b ); # '=>' or ',' but not '='
print "$a\n"; # prints 1
$b = 2;
print "$a\n"; # prints 2
You can lookup the documentation for tie, but in a nutshell, it allows you to define your own implementation of a variable (for scalars, arrays, hashes, or file handles). So this code creates the new variable $a with an implementation that gets or sets the current value of $b (by storing a reference to $b internally). The new method is not strictly needed (the constructor is actually TIESCALAR) but is provided as syntactic sugar to avoid having to use tie directly in the calling code.
(which would be tie my $a, 'Lazy', $b;)
You wish to pretend that $a refers to something that is evaluated when $a is used... You can only do that if $a is not truly a scalar, it could be a function (as cHao's answer) or, in this simple case, a reference to the other variable
my $b="1";
my $a= \$b;
$b="2";
print $$a;
I would like the ${b} to remain "unreplaced" until $a is needed.
Then I'd recommend eschewing string interpolation, instead using sprintf, so that you "interpolate" when needed.
Of course, on this basis you could tie together something quick(ish) and dirty:
use strict;
use warnings;
package LazySprintf;
# oh, yuck
sub TIESCALAR { my $class = shift; bless \#_, $class; }
sub FETCH { my $self = shift; sprintf $self->[0], #$self[1..$#$self]; }
package main;
my $var = "foo";
tie my $lazy, 'LazySprintf', '%s', $var;
print "$lazy\n"; # prints "foo\n"
$var = "bar";
print "$lazy\n"; # prints "bar\n";
Works with more exotic format specifiers, too. Yuck.
#!/usr/bin/perl
use strict;
use warnings;
my #array = qw[a b c];
foreach my($a,$b,$c) (#array) {
print "$a , $b , $c\n";
}
I receive following error:
Missing $ on loop variable
What is wrong?
I am using: perl v5.10.1 (*) built for x86_64-linux-thread-multi
To grab multiple list items per iteration, use something like List::MoreUtils::natatime
or use splice:
my #tmparray = #array; # don't trash original array
while ( my ($a,$b,$c) = splice(#tmparray,0,3) ) {
print "$a , $b , $c\n";
}
Or reorganize your data into multiple arrays and use one of the Algorithm::Loops::MapCar* functions to loop over multiple arrays at once.
I'm not aware that foreach can eat up more than one parameter at a time in Perl. I might be reading the documentation wrong.
As mentioned in the other answers, Perl does not directly support iterating over multiple values in a for loop. This is one of the issues I addressed in my module List::Gen:
use List::Gen;
my #array = qw/a b c d e f/;
for (every 3 => #array) {
print "#$_\n";
}
outputs:
a b c
d e f
Edit: The list slices that List::Gen produces are aliased to the original list, so that means you can change values in the slice to change the original list. That functionality does not seem possible with several of the other solutions posted for this question.
Block::NamedVar provides nfor that DWIM. This is more convenient than the alternative ways to iterate.
use Block::NamedVar;
my #array = qw[a b c];
nfor my($a,$b,$c) (#array) {
print "$a , $b , $c\n";
}
I think you are missing the point of foreach loop. It goes through every value in an array. It handles one value at a time. Here's the correct code:
#!/usr/bin/perl
use strict;
use warnings;
my #array = qw[a b c];
foreach my $z (#array) {
print "$z\n";
}