How do I use an incremented index in Perl's map function? - perl

I'd like to use an incremented index in Perl, in the map function. The code I have is:
use strict;
my $ord = "46.15,18.59,47.45,21.14";
my $c = 1;
my #a = split(",",$ord);
my $str = join("\n", map "x($c++) := $_;", #a);
print $str;
This outputs:
x(1++) := 46.15;
x(1++) := 18.59;
x(1++) := 47.45;
x(1++) := 21.14;
Instead of the x(1++), I would like x(1), x(2), etc.
How can I reach it?

Instead of mapping the array, you can map your count, and not need a separate variable:
my $str = join("\n", map "x($_) := $a[$_-1];", 1..#a);
Or, to include a trailing newline:
my $str = join('', map "x($_) := $a[$_-1];\n", 1..#a);

Your problem has nothing to do with map. You placed Perl code inside a string literal and hoped it would get executed.
Replace
map "x($c++) := $_;",
with
map { ++$c; "x($c) := $_;" }
Also, you are missing a trailing newline. Fixed:
my $str = join "", map { ++$c; "x($c) := $_;\n" } #a;
print $str;
or
print map { ++$c; "x($c) := $_;\n" } #a;

It seems, that concatenating is the answer:
my $str = join("\n", map "x(".$c++.") := $_;", #a);

Related

Why function does not receive arguments?

I have next code:
my $str = '';
new( (split ',', $str )[0] )
Here I split my $str and asks exactly one element from result list
But when check incoming data at #_ I see zero elements
Why function does not receive arguments?
I expect one element
Here is some code that tests what you say in your question.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
sub new {
say 'new() received ' . #_ . ' argument(s).';
say "The first argument was '$_[0].'" if #_;
}
my $str = 'one,two,three';
new( (split ',', $str )[0] );
When I run it, I get the following output:
$ perl split_test
new() received 1 argument(s).
The first argument was 'one.'
This seems to be working as expected. So it seems likely that your problem lies in parts of the code that you haven't shared with us.
It seems I found the answer.
Problem was because of special case when I slice empty list.
This special case is useful at while condition:
while ( ($home, $user) = (getpwent)[7,0] ) {
printf "%-8s %s\n", $user, $home;
}
Here is documentation for this
#a = ()[0,1]; # #a has no elements
#b = (#a)[0,1]; # #b has no elements
#c = (sub{}->())[0,1]; # #c has no elements
#d = ('a','b')[0,1]; # #d has two elements
#e = (#d)[0,1,8,9]; # #e has four elements
#f = (#d)[8,9]; # #f has two elements

Alternate between upper and lowercase, PERL

I want to alternate between upper and lower case, however I only managed to get the whole string upper or lower, or the first character.
I have not found a proper function to execute what I need. Please have a look and help me out. Cheers.
#!/usr/bin/perl
my $mystring = "this is my string I want each character to alternate between upper and lowercase";
my #myarray = split("", $mystring);
print ucfirst("#myarray");
A more general approach using function factory
use strict;
use warnings;
sub periodic {
my #subs = #_;
my $i = 0;
return sub {
$i = 0 if $i > $#subs;
return $subs[$i++]->(#_);
};
}
my $mystring = "this is my string I want each character to alternate between upper and lowercase";
my $f = periodic(
sub { uc pop },
sub { lc pop },
# sub { .. },
# sub { .. },
);
$mystring =~ s/([a-z])/ $f->($1) /egi;
print $mystring, "\n";
output
ThIs Is My StRiNg I wAnT eAcH cHaRaCtEr To AlTeRnAtE bEtWeEn UpPeR aNd LoWeRcAsE
How about:
my $mystring = "this is my string I want each character to alternate between upper and lowercase";
my #myarray = split("", $mystring);
my $cnt = 1;
for (#myarray) {
next unless /[a-z]/i;
$_ = ($cnt%2 ? uc($_) : lc($_));
$cnt++;
}
say join('',#myarray);
Output:
ThIs Is My StRiNg I wAnT eAcH cHaRaCtEr To AlTeRnAtE bEtWeEn UpPeR aNd LoWeRcAsE
My first thought was to use a regex substitution. Try this:
use strict;
use warnings;
my $str = "this string, I will change";
# Ignore whitespace and punctuation.
$str =~ s/(\w)(\w)/\L$1\U$2/g;
# Or include all characters in the uc/lc alternation.
# $str =~ s/(.)(.)/\L$1\U$2/g;
print $str, "\n";
If, for some reason, you wish to avoid regexes, try:
my $str = "this string, I will change";
my #ary;
my $count = 0;
for my $glyph ( split //, lc $str ) {
$glyph = uc $glyph if $count % 2;
push #ary, $glyph;
$count++;
}
print join( "", #ary ), "\n";
Try this:
use strict;
use warnings;
use 5.016;
use Data::Dumper;
my $str = 'hello';
my $x = 0;
$str =~ s/(.)/($x++ % 2 == 0) ? "\U$1" : "\L$1"/eg;
say $str;
--output:--
HeLlO
Save script below with name alter.pl
#!/usr/bin/perl
print#ARGV[0]=~s/([a-z])([^a-z]*)([a-z])/uc($1).$2.lc$3/egri
And run script by command
$ perl alter.pl "this is my string I want each character to alternate between upper and lowercase"
Output
ThIs Is My StRiNg I wAnT eAcH cHaRaCtEr To AlTeRnAtE bEtWeEn UpPeR aNd LoWeRcAse
You have some good answers already but I thought I'd chip in because I hadn't seen map yet.
print map { $c++ % 2 ? lc : uc } split ( //, $mystring );
splits $mystring into characters (split //);
uses map to apply a function to each letter.
uses $c++ to autoincrement, then take a modulo 2 to decide if this should be uppercase or lower case.
join the resultant array.
Gives:
#!c:\Strawberry\perl\bin
use strict;
use warnings;
my $mystring = "this is my string I want each character to alternate between upper and lowercase";
my $c;
print join ( "", map { $c++ % 2 ? lc : uc } split ( //, $mystring ));
Prints:
ThIs iS My sTrInG I WaNt eAcH ChArAcTeR To aLtErNaTe bEtWeEn uPpEr aNd lOwErCaSe
map is a useful function that applies some code to each element in a list, and then 'returns' the list that's produced. So if we treat your string as a list of characters, it works nicely.
Try this. simple if else condition enough for this
my $mystring = "this is my string I want each character to alternate between upper and lowercase";
#xz = split( '', $mystring );
for ( $i = 0; $i < scalar #xz; $i++ ) {
if ( $i % 2 ) {
print uc "$xz[$i]";
}
else {
print "$xz[$i]";
}
}

How to obtain value of Perl regex match variable with index stored in another variable?

I have a subroutine that takes as input the a position in a string, and should return the word found at that position. For example:
use warnings;
use strict;
my $num=2;
my $val=getMatch($num);
sub getMatch {
my ($num)=#_;
my $str='a b c';
$str=~ /(\S+)\s(\S+)/;
my $res;
eval "$res=\$$num";
return $res
}
But this gives error:
Use of uninitialized value $res in concatenation (.) or string at ./p.pl line 16.
(I am trying to return $i where i is a value stored in another variable..)
I'd do:
my $num=2;
my $val=getMatch($num);
say $val;
sub getMatch {
my ($num)=#_;
my $str='a b c';
my #res = $str =~ /(\S+)\s(\S+)/;
return $res[$num-1];
}
Output:
b
You could use the #+ and #- special variables, documented in perlvar, like this:
sub getMatch {
my ($num)=#_;
my $str='a b c';
$str=~ /(\S+)\s(\S+)/;
return substr( $str, $-[$num], $+[$num] - $-[$num] );
}
print getMatch(1), "\n";
print getMatch(2), "\n";
Or you could adjust your regex like this:
sub getMatch {
my $num = shift() - 1;
my $str='a b c';
$str=~ /(?:\S+\s){$num}(\S+)/;
return $1;
}
print getMatch(1), "\n";
print getMatch(2), "\n";
...which has the advantage of producing only a single capture group.
Another option is to just split on space:
sub getMatch {
my ($num)=#_;
my $str='a b c';
return ( split /\s/, $str )[$num-1];
}
print getMatch(1), "\n";
print getMatch(2), "\n";
...but that last solution is more permissive as to what it will match; it doesn't explicitly require two or more non-space items separated by a space. If 3 were passed in, it would return 'c'.
This last one produces results similar to the split version but using a regex. I'd probably prefer the split because it's more straightforward, but I provide this just for edification:
sub getMatch {
my ($num)=#_;
my $str='a b c';
return ( $str =~ m/(\S+)(?=\s|$)/g )[$num-1];
}
print getMatch(1), "\n";
print getMatch(2), "\n";

Why can't my Perl code implement the reverse function?

Here is my code named reverse.pl
#!usr/bin/perl -w
use 5.016;
use strict;
while(my $line=<>)
{
my #array=();
push (#array,$line);
#array=reverse#array;
say #array;
}
Test file named a.txt
A B C D
E F G H
I J K L
M N O P
Q R S T
My command is perl reverse.pl a.txt
Why it can't implement the reverse function?
I want to show the result is:
D C B A
H G F E
and so on.
Reverse in a scalar context reverses a scalar.
Reverse in a list context reverses the list, but not each scalar within the list.
You explicitly turn your scalar $line into a list with one item and then reverse the order of the items.
Try this:
#!/usr/bin/perl
use 5.016;
use strict;
while (my $line=<>) {
chomp($line);
say scalar reverse $line;
}
If you have an array and want to reverse each element (but not the elements), use map:
my #array = qw(Alpha Beta Gamma);
#array = map { scalar reverse $_ } #array;
print "#array\n";
If you want to do both (reverse each element and the elements themselves), do:
#array = map { scalar reverse $_ } reverse #array;
or:
#array = reverse map { scalar reverse $_ } #array;
When you say:
push #array, $line;
You're creating an array of one value that's equal to the line.
$array[0] = "A B C D";
When you say:
#array = reverse #array;
You are reversing that single member array. The first element becomes the last, and the last element becomes the first, etc.. However, you only have one element, so there's nothing to reverse.
What you want to do is create an array with your line:
my #array = split /\s+/, $line;
This will create an array with each character being a separate element of the array. For example, your first line:
$array[0] = "A";
$array[1] = "B";
$array[2] = "C";
$array[3] = "D";
Now, if you use reverse on this array, you'll get:
$array[0] = "D";
$array[1] = "C";
$array[2] = "B";
$array[3] = "A";
Here's the program:
use strict;
use warnings;
use feature qw(say);
while ( my $line = <> ) {
chomp $line;
my #array = split /\s+/, $line;
say join " ", reverse $line;
}
The join function takes an array, and joins each element into a single line -- thus rebuilding your line.
By the way, I could have done this:
#array = reverse #array;
say "#array"; #Quotes are important!
This is because Perl will automatically join an array with whatever character is in $". This is a Perl variable that is used for joining arrays when that array is placed in quotation marks, and the default value is a single space.
Personally, I rather prefer the say join " ", reverse $line;. It's more obvious what is going on, and doesn't depend upon the value of rarely used variables.

Reformulate a string query in perl

How do i reformulate a string in perl?
For example consider the string "Where is the Louvre located?"
How can i generate strings like the following:
"the is Louvre located"
"the Louvre is located"
"the Louvre located is"
These are being used as queries to do a web search.
I was trying to do something like this:
Get rid of punctuations and split the sentence into words.
my #words = split / /, $_[0];
I don't need the first word in the string, so getting rid of it.
shift(#words);
And then i need move the next word through out the array - not sure how to do this!!
Finally convert the array of words back to a string.
How can I generate all permutations of an array in Perl?
Then use join to glue each permutation array back together into a single string.
Somewhat more verbose example:
use strict;
use warnings;
use Data::Dumper;
my $str = "Where is the Louvre located?";
# split into words and remove the punctuation
my #words = map {s/\W+//; $_} split / /, $str;
# remove the first two words while storing the second
my $moving = splice #words, 0 ,2;
# generate the variations
my #variants;
foreach my $position (0 .. $#words) {
my #temp = #words;
splice #temp, $position, 0, $moving;
push #variants, \#temp;
}
print Dumper(\#variants);
my #head;
my ($x, #tail) = #words;
while (#tail) {
push #head, shift #tail;
print join " ", #head, $x, #tail;
};
Or you can just "bubble" $x through the array: $words[$n-1] and words[$n]
foreach $n (1..#words-1) {
($words[$n-1, $words[$n]) = ($words[$n], $words[$n-1]);
print join " ", #words, "\n";
};