Is there a way to do a "reverse" .=? - perl

I want to add 0 to the front of $num:
$num = "0".$num;
This works fine, but is there a way to do it with $num.= plus some sort of operator or something to tell it to concatenate in the opposite way? Or any other way to do it, in general?

No. Assignment operators don't work that way.
$a .= "foo";
...always means something similar to
$a = $a . "foo";
There is no operator for "concatenate these strings and reverse the order", and so there is no corresponding assignment operator.

Insert 0 in front of $num,
$num =~ s|^|0|;

There is no prepend operator, but you can do this.
Assume $num is not defined yet.
my $num = '0';
$num .= expr-that-generates-the-number;
So you're effectively prepending 0 to the variable without having to type the variable twice.

If your concern is to do this frequently, you could dump the string elements into an array and then reverse it? But you won't necessarily be reducing the amount of code, but may be easier to keep track of everything.
$array = array();
array_push ( $array , "test" );
array_push ( $array , 0 );
$array = array_reverse( $array );
echo implode( "" , $array );

Related

Concatenating elements from two arrays of same indexes

I am facing an issue while concatenating elements of indexes from two arrays.
Example:
#x=(1,2,3,4);
#y=(5,6,7,8);
I want to concatenate
$x[0]"_"$y[0]
Like this:
if #i=(..n), then $x[$i]"_"$y[$i]
Suggest the possible solution.
To repeat the process for n elements in the array, you can do the following
my #x=(1,2,3,4);
my #y=(5,6,7,8);
my #concatenated_array=();
for my $i (0 .. $n) # define $n <= min($#x,$#y)
{
push #concatenated_array, $x[$i] ."_". $y[$i];
}
print "#concatenated_array\n";
In Perl, you can concatenate elements like this:
my #x=(1,2,3,4);
my #y=(5,6,7,8);
my $z = $x[0] . $y[0];
If you want use an underscore sign between two elements you can use like this:
my $z = $x[0] ."_". $y[0];
In perl concatenation is done using . So if you want to concatenate two elements use
my $z = $x[0] . $y[0]
If you want to concatenate many elements with each other best is to do this in loop and keep on concatenating $z with element $x[0] or $y[0] and assigning it back to $z
my $z = $z . $x[0]
$z = $z . $y[0]
I do not know your exact condition on which you will be concatenating so i kind give you exact answer but if you use above logic with a loop i hope your problem will be solved.

Perl, concatenation of 0s

I have the following code:
foreach $string (#strings) {
if ($hash{$string}) {
$letter = $hash{$string};
$text .= $letter;
}
}
where $letter is one character or one number (for example: a, e, o, 2, 5...). It creates a fine text, but I have problems with appending the number 0.
If $letter = 0 it is not concatenated, giving for example 28 instead of 2008. However, if $letter at that moment is 1 or aaaa, it gives 2118 or 2aaaaaaaa8.
I was thinking that it might be something related to 0 and FALSE in Perl... might be?
Many thanks.
You test to see if the entry exists using if ($hash{$string}) but that also checks that the value is true. Use if (defined $hash{$string}) instead.
I was thinking that it might be something related to 0 and FALSE in Perl... might be?
Yes, that is the issue.
The correct way to check whether a hash key is defined as follows
foreach $string (#strings)
{
if (defined $hash{$string}) # Check if key is defined
{
$letter = $hash{$string};
$text .= $letter;
}
}
A value of 0 is defined but false. Check definedness rather than truth:
if (defined $hash{$string}) {
or using the defined-or operator:
$text .= $hash{$string} // '';

foreach loop with a condition perl?

Is it possible to have a foreach loop with a condition in Perl?
I'm having to do a lot of character by character processing - where a foreach loop is very convenient. Note that I cannot use some libraries since this is for school.
I could write a for loop using substr with a condition if necessary, but I'd like to avoid that!
You should show us some code, including the sort of thing you would like to do.
In general, character-by-character processing of a string would be done in Perl by writing
for my $ch (split //, $string) { ... }
or, if it is more convenient
my #chars = split //, $string;
for (#chars) { ... }
or
my $i = 0;
while ($i < #chars) { my $char = $chars[$i++]; ... }
and the latter form can support multiple expressions in the while condition. Perl is very rich with different ways to do the similar things, and without knowing more about your problem it is impossible to say which is best for you.
Edit
It is important to note that none of these methods allow the original string to be modified. If that is the intention then you must use s///, tr/// or substr.
Note that substr has a fourth parameter that will replace the specified part of the original string. Note also that it can act as an lvalue and so take an assignment. In other words
substr $string, 0, 1, 'X';
can be written equivalently as
substr($string, 0, 1) = 'X';
If split is used to convert a string into a list of characters (actually one-character strings) then it can be modified in this state and recombined into a string using join. For instance
my #chars = split //, $string;
$chars[0] = 'X';
$string = join '', #chars;
does a similar thing to the above code using substr.
For example:
foreach my $l (#something) {
last if (condition);
# ...
}
will exit the loop if condition is true
You might investigate the next and last directives. More info in perldoc perlsyn.

Iterating over data structure

I am trying to iterate over this data structure:
$deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}[0]->{code}
where fact[0] is increasing. It's several files I am processing so the number of {facts}[x] varies.
I thought this might work but it doesn't seem to be stepping up the $iter var:
foreach $iter(#{$deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}}){
print $deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}[$iter]->{code}."\n";
}
I'm totally digging data structures but this one is stumping me. Any advice what might be wrong here?
$iter is being set to the content of each item in the array not the index. e.g.
my $a = [ 'a', 'b', 'c' ];
for my $i (#$a) {
print "$i\n";
}
...prints:
a
b
c
Try:
foreach $iter (#{$deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}}){
print $iter->{code}."\n";
}
$iter is not going to be an index that you can subscript the array with, it is rather the current element of the array. So I guess you should be fine with:
$iter->{code}
Your $iter contains the data sctructure. What you basiclly want is:
foreach my $elem ( #{$deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}} ){
print $elem->{code};
}
or:
foreach my $iter ( 0 .. scalar #{$deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}} ){
print $deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}[$iter]->{code}."\n";
}
Since you are looping over the array, your misnamed $iter is the value you are looking for, not an index.
If you want to loop over the indexes instead, do:
foreach $iter ( 0 .. $#{$deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}} ) {
print "Index $iter: ",
$deconstructed->{data}->{workspaces}[0]->{workspace}->{facts}[$iter]->{code}."\n";
}
Also note that you can drop -> between two [] or {}:
$deconstructed->{data}{workspaces}[0]{workspace}{facts}[$iter]{code}
I recommend reading http://perlmonks.org/?node=References+quick+reference.
When you have ugly data structures like this, make an interface for it so your life is easier:
foreach my $fact ( $data_obj->facts ) { # make some lightweight class for this
....;
}
Even without that, consider using a reference to just the part of the data structure you need so you don't think about the rest:
my $facts = $deconstructed->{data}{workspaces}[0]{workspace}{facts};
foreach my $fact ( #$facts ) {
print "Thing is $fact->{code}\n";
}
It's just a reference, so you're not recreating anything. Since you only have to think about the parts beyond the facts key, the problem doesn't look as hard.

How can I expand a string like "1..15,16" into a list of numbers?

I have a Perl application that takes from command line an input as:
application --fields 1-6,8
I am required to display the fields as requested by the user on command line.
I thought of substituting '-' with '..' so that I can store them in array e.g.
$str = "1..15,16" ;
#arr2 = ( $str ) ;
#arr = ( 1..15,16 ) ;
print "#arr\n" ;
print "#arr2\n" ;
The problem here is that #arr works fine ( as it should ) but in #arr2 the entire string is not expanded as array elements.
I have tried using escape sequences but no luck.
Can it be done this way?
If this is user input, don't use string eval on it if you have any security concerns at all.
Try using Number::Range instead:
use Number::Range;
$str = "1..15,16" ;
#arr2 = Number::Range->new( $str )->range;
print for #arr2;
To avoid dying on an invalid range, do:
eval { #arr2 = Number::Range->new( $str )->range; 1 } or your_error_handling
There's also Set::IntSpan, which uses - instead of ..:
use Set::IntSpan;
$str = "1-15,16";
#arr2 = Set::IntSpan->new( $str )->elements;
but it requires the ranges to be in order and non-overlapping (it was written for use on .newsrc files, if anyone remembers what those are). It also allows infinite ranges (where the string starts -number or ends number-), which the elements method will croak on.
You're thinking of #arr2 = eval($str);
Since you're taking input and evaluating that, you need to be careful.
You should probably #arr2 = eval($str) if ($str =~ m/^[0-9.,]+$/)
P.S. I didn't know about the Number::Range package, but it's awesome. Number::Range ftw.
I had the same problem in dealing with the output of Bit::Vector::to_Enum. I solved it by doing:
$range_string =~ s/\b(\d+)-(\d+)\b/expand_range($1,$2)/eg;
then also in my file:
sub expand_range
{
return join(",",($_[0] .. $_[1]));
}
So "1,3,5-7,9,12-15" turns into "1,3,5,6,7,9,12,13,14,15".
I tried really hard to put that expansion in the 2nd part of the s/// so I wouldn't need that extra function, but I couldn't get it to work. I like this because while Number::Range would work, this way I don't have to pull in another module for something that should be trivial.
#arr2 = ( eval $str ) ;
Works, though of course you have to be very careful with eval().
You could use eval:
$str = "1..15,16" ;
#arr2 = ( eval $str ) ;
#arr = ( 1..15,16 ) ;
print "#arr\n" ;
print "#arr2\n" ;
Although if this is user input, you'll probably want to do some validation on the input string first, to make sure they haven't input anything dodgy.
Use split:
#parts = split(/\,/, $fields);
print $parts[0];
1-6
print $parts[1];
8
You can't just put a string containing ',' in an array, and expect it to turn to elements (except if you use some Perl black magic, but we won't go into that here)
But Regex and split are your friends.