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.
Related
Small debug question I can't solve for some reason. Consider the following code:
use warnings;
my $flag = 0;
foreach my $i (0..scalar(#ARGV)) {
$data{$OPTION} .= $ARGV[$i]." " if($flag);
$flag = 1 if($ARGV[$i] =~ /$OPTION/);
undef $ARGV[$i] if($flag);
}
I get the following two warnings:
Use of uninitialized value within #ARGV in concatenation (.) or string at line 4
Use of uninitialized value in pattern match (m//) at line 5
I get the reason is that I undefine some value of #ARGV and then it tries to check it.
The way I do it like this is because I would like to 'cut' some of the data of #ARGV before using GetOpt module (which uses this array).
How to solve it?
Let's expand on those comments a bit.
Imagine #ARGV contains four elements. They will have the indexes 0, 1, 2 and 3 (as arrays in Perl are zero-based).
And your loop looks like this:
foreach my $i (0..scalar(#ARGV)) {
You want to visit each element in #ARGV, so you use the range operator (..) to generate a list of all those indexes. But scalar #ARGV returns the number of elements in #ARGV and that's 4. So your range is 0 .. 4. And there's no value at $ARGV[4] - so you get an "undefined value" warning (as you're trying to read past the end of an array).
A better way to do this is to use $#ARGV instead of scalar #ARGV. For every array variable in Perl (say #foo) you also get a variable (called $#foo) which contains the last index number in the array. In our case, that's 3 and your range (0 .. $#ARGV) now contains the integers 0 .. 3 and you no longer try to read past the end of the array and you don't get the "undefined value" warnings.
There's one other improvement I would suggest. Inside your loop, you only ever use $i to access an element from #ARGV. It's only used in expressions like $ARGV[$i]. In this case, it's probably better to skip the middle man and to iterate across the elements in the array, not the indexes.
I mean you can write your code like this:
foreach my $arg (#ARGV) {
$data{$OPTION} .= $arg . " " if($flag);
$flag = 1 if($arg =~ /$OPTION/);
undef $arg if($flag);
}
I think that's a little easier to follow.
This question already has answers here:
Why do you need $ when accessing array and hash elements in Perl?
(9 answers)
Closed 8 years ago.
Today I start my perl journey, and now I'm exploring the data type.
My code looks like:
#list=(1,2,3,4,5);
%dict=(1,2,3,4,5);
print "$list[0]\n"; # using [ ] to wrap index
print "$dict{1}\n"; # using { } to wrap key
print "#list[2]\n";
print "%dict{2}\n";
it seems $ + var_name works for both array and hash, but # + var_name can be used to call an array, meanwhile % + var_name can't be used to call a hash.
Why?
#list[2] works because it is a slice of a list.
In Perl 5, a sigil indicates--in a non-technical sense--the context of your expression. Except from some of the non-standard behavior that slices have in a scalar context, the basic thought is that the sigil represents what you want to get out of the expression.
If you want a scalar out of a hash, it's $hash{key}.
If you want a scalar out of an array, it's $array[0]. However, Perl allows you to get slices of the aggregates. And that allows you to retrieve more than one value in a compact expression. Slices take a list of indexes. So,
#list = #hash{ qw<key1 key2> };
gives you a list of items from the hash. And,
#list2 = #list[0..3];
gives you the first four items from the array. --> For your case, #list[2] still has a "list" of indexes, it's just that list is the special case of a "list of one".
As scalar and list contexts were rather well defined, and there was no "hash context", it stayed pretty stable at $ for scalar and # for "lists" and until recently, Perl did not support addressing any variable with %. So neither %hash{#keys} nor %hash{key} had meaning. Now, however, you can dump out pairs of indexes with values by putting the % sigil on the front.
my %hash = qw<a 1 b 2>;
my #list = %hash{ qw<a b> }; # yields ( 'a', 1, 'b', 2 )
my #l2 = %list[0..2]; # yields ( 0, 'a', 1, '1', 2, 'b' )
So, I guess, if you have an older version of Perl, you can't, but if you have 5.20, you can.
But for a completist's sake, slices have a non-intuitive way that they work in a scalar context. Because the standard behavior of putting a list into a scalar context is to count the list, if a slice worked with that behavior:
( $item = #hash{ #keys } ) == scalar #keys;
Which would make the expression:
$item = #hash{ #keys };
no more valuable than:
scalar #keys;
So, Perl seems to treat it like the expression:
$s = ( $hash{$keys[0]}, $hash{$keys[1]}, ... , $hash{$keys[$#keys]} );
And when a comma-delimited list is evaluated in a scalar context, it assigns the last expression. So it really ends up that
$item = #hash{ #keys };
is no more valuable than:
$item = $hash{ $keys[-1] };
But it makes writing something like this:
$item = $hash{ source1(), source2(), #array3, $banana, ( map { "$_" } source4()};
slightly easier than writing:
$item = $hash{ [source1(), source2(), #array3, $banana, ( map { "$_" } source4()]->[-1] }
But only slightly.
Arrays are interpolated within double quotes, so you see the actual contents of the array printed.
On the other hand, %dict{1} works, but is not interpolated within double quotes. So, something like my %partial_dict = %dict{1,3} is valid and does what you expect i.e. %partial_dict will now have the value (1,2,3,4). But "%dict{1,3}" (in quotes) will still be printed as %dict{1,3}.
Perl Cookbook has some tips on printing hashes.
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 );
I am trying to write a function in Perl that computes the cross product (cartesian product) of two Strings. I have similar code in Python that looks like this:
def cross(A, B):
"Cross product of elements in A and elements in B."
return [a+b for a in A for b in B]
How could I mimic this list comprehension in an elegant way?
Here is what I have so far:
# compute the cross product of two Strings
# cross('12','AB') = ((1,A), (1,B), (2,A), (2,B))
sub cross {
# unpack strings
my ($A, $B) = #_;
# array to hold products
my #out_array;
# split strings into arrays
my #A_array = split(//, $A);
my #B_array = split(//, $B);
# glue the characters together and append to output array
for my $r (#A_array) {
for my $c (#B_array) {
push #out_array, [$r . $c];
}
}
return \#out_array;
}
This isn't working exactly as I would expect, for some reason a reference is coming back from split() instead of a List.
Any suggestions or other more elegant cartesian product solutions would be appreciated.
Your problem is in this part:
push #out_array, [$r . $c];
$r . $c concatenates the two scalars to a string. [EXPR] creates an array reference. You don't want a reference, just plain strings:
push #out_array, $r . $c;
If you don't like push, but syntactic sugar, you can use a module that implements gather/take:
my #cross = gather {
for my $x (#A) {
for my $y (#B) {
take $x . $y;
}
}
};
This is implemented e.g. by List::Gather or Syntax::Keyword::Gather.
I myself am fond of elaborate map expressions:
my #cross = map { my $x = $_; map $x.$_, #B } #A;
(same as for with push for all practical purposes).
Note: Perl does not have a concept of “characters” that is related to arrays. When single characters are needed, these are modelled by strings of length 1. Perl arrays always contain scalars, but for (memory) performance reasons strings are not implemented as Perl arrays, but as a pointer to a C array (of known length). The downside is different sets of operations for strings and arrays, the upside is less memory usage.
As characters are just very short strings, to join them we use standard string concatenation with ..
This question has been asked about PHP both here and here, and I have the same question for Perl. Given a function that returns a list, is there any way (or what is the best way) to immediately index into it without using a temporary variable?
For example:
my $comma_separated = "a,b,c";
my $a = split (/,/, $comma_separated)[0]; #not valid syntax
I see why the syntax in the second line is invalid, so I'm wondering if there's a way to get the same effect without first assigning the return value to a list and indexing from that.
Just use parentheses to define your list and then index it to pull your desired element(s):
my $a = (split /,/, $comma_separated)[0];
Just like you can do this:
($a, $b, $c) = #array;
You can do this:
my($a) = split /,/, $comma_separated;
my $a on the LHS (left hand side) is treated as scalar context. my($a) is list context. Its a single element list so it gets just the first element returned from split.
It has the added benefit of auto-limiting the split, so there's no wasted work if $comma_separated is large.