how perl array works $" - arrays

I am confused about the outcome of this code.
my #lines;
for (my $count = 0; $count < 3; $count++) {
print "Give me input again ";
chomp (my $line = <STDIN>);
$lines[$count] = $line;
}
$" = "|";
print "#lines\n";
When I run the code, how does this: $" = "|"; work?
The results are One|Two|Three. How does the code work so that it puts "|" between each input?

It's simply what interpolation of arrays into double-quoted strings does.
"$foo\n"
is identical to
$foo . "\n"
and
"#lines\n"
is identical to
join($", #lines) . "\n"
This is documented in perldata and in perlvar.

$" is just a special variable name in perl that tells the interpreter how to separate array elements in double-quoted string context. The default value is a space, but the above code tells perl to use | instead. Hence One|Two|Three instead of the default of One Two Three if you left out that line.
See http://perldoc.perl.org/perlvar.html#General-Variables for more detail.

Related

Convert array to a string with newlines embeded in string

i need to convert a perl array to a single string variable, containing all array items separated with newlines.
my $content = "";
#names=('A','C','C','D','E');
$content = $content . join($", #names) . "\n";
print $content;
I intend the output to be like:
A
C
C
D
E
But i get:
A C C D E
Why isn't the newline \n character being honoured?
Since it appears that you want a newline not just between each line, but after the last one too, you can use any of the following:
join("\n", #names) . "\n"
join("", map "$_\n", #names)
join("\n", #names, "")
These are equivalent except when the array in empty. In that situation, the first results in a newline, and the other result in an empty string.
By the way,
$content = $content . EXPR;
can be written as
$content .= EXPR;
To join an array with newlines in between, use
join("\n", #array)
Your code uses the contents of the $" variable as the separator, which by default contains a space.
Do this instead:
$content = $content . join("\n", #names);
The $" variable holds the value that Perl uses between array elements when it interpolates an array in a double-quoted string. By default (and you haven't changed it), it's a space.
Perhaps you were thinking about the input or output record separators, $/ or $\.
But, you don't really want to play with those variables. If you want a newline, use a newline:
join "\n", #array;

Perl output successive string from array

I have an array I can print out as "abcd" however I am trying to print it as "a>ab>abc>abcd". I can't figure out the nested loop I need within the foreach loop I have. What loop do I need within it to print it this way?
my $str = "a>b>c>d";
my #words = split />/, $str;
foreach my $i (0 .. $#words) {
print $words[$i], "\n";
}
Thank you.
You had the right idea, but instead of printing the word at position i, you want to print all the words between positions 0 and i (inclusive). Also, your input can contain multiple strings, so loop over them.
use warnings;
while (my $str = <>) { # read lines from stdin or named files
chomp($str); # remove any trailing line separator
my #words = split />/, $str; # break string into array of words
foreach my $i (0 .. $#words) {
print join '', #words[0 .. $i]; # build the term from the first n words
print '>' if $i < $#words; # print separator between terms (but not at end)
}
print "\n";
}
There are many other ways to write it, but hopefully this way helps you understand what's happening and why. Good luck!
one liner:
perl -e '#a=qw(a b c d); for(#a) {$s.=($h.=$_).">"} $s=substr($s,0,-1);print $s'
I would do it like this:
#!/usr/bin/perl
use strict;
use warnings;
my $str = "a>b>c>d>e>f>g";
my #words = split />/, $str;
$" = '';
my #new_words;
push #new_words, "#words[0 .. $_]" for 0 .. $#words;
print join '>', #new_words;
A few things to explain.
Perl will expand array variables in a double-quoted string. So something like this:
#array = ('x', 'y', 'z');
print "#array";
will print x y z. Notice there are spaces between the elements. The string that is inserted between the elements is controlled by the $" variable. So by setting that variable to an empty string we can remove the spaces, so:
$" = '';
#array = ('x', 'y', 'z');
print "#array";
will print xyz.
The most complex line is:
push #new_words, "#words[0 .. $_]" for 0 .. $#words;
That's just a compact way to write:
for (0 .. $#words) {
my $new_word = "#words[0 .. $_]";
push #new_words, $new_word;
}
We iterate across the integers from zero to the last index in #words. Each time around the loop, we use an array slice to get a list of elements from the array, convert that to a string (by putting it in double-quotes) and then push that string onto #new_words.
This is what I ended up with, It's the only way I could understand and get the output I was looking for.
use strict;
use warnings;
my $str = "a>b>c>d>e>f>g";
my #words = split />/, $str;
my $j = $#words;
my $i = 0;
my #newtax;
while($i <= $#words){
foreach my $i (0 .. $#words - $j){
push (#new, $words[$i]);
}
if($i < $#words){
push(#new, ">");
}
$j--;
$i++;
}
print #new;
This output "a>ab>abc>abcd>abcde>abcdef>abcdefg"

Accessing array elements in shell

I have an array as:
Option[0]=$3
Option[1]=$4
Option[2]=$5
Option[3]=$6
Option[4]=$7
Option[5]=$8
I have to access array elements in shell script.
I know the format is "${Option[0]}" but this format is not accepted in my system.
Its giving me error "Bad substitution".
I have found the solution as:
Arrays are not possible in Busybox shell but a tweak is possible like:
Option="$3; $4; $5; $6; $7; $8"
I have used ; because some of the elements were having "space in the string". Like $3 = " Hello World"
And while iterating the array, delimeter was a space itself.
So to modify the delimiter ; was used.
For iterating the above array:
IFS=$';'
for opt in $Option
do
if [ $opt != " " ]; then
echo "$opt"
fi
done

Comparing two strings line by line in Perl

I am looking for code in Perl similar to
my #lines1 = split /\n/, $str1;
my #lines2 = split /\n/, $str2;
for (int $i=0; $i<lines1.length; $i++)
{
if (lines1[$i] ~= lines2[$i])
print "difference in line $i \n";
}
To compare two strings line by line and show the lines at which there is any difference.
I know what I have written is mixture of C/Perl/Pseudo-code. How do I write it in the way that it works on Perl?
What you have written is sort of ok, except you cannot use that notation in Perl lines1.length, int $i, and ~= is not an operator, you mean =~, but that is the wrong tool here. Also if must have a block { } after it.
What you want is simply $i < #lines1 to get the array size, my $i to declare a lexical variable, and eq for string comparison. Along with if ( ... ) { ... }.
Technically you can use the binding operator to perform a string comparison, for example:
"foo" =~ "foobar"
But it is not a good idea when comparing literal strings, because you can get partial matches, and you need to escape meta characters. Therefore it is easier to just use eq.
Using C-style for loops is valid, but the more Perl-ish way is to use this notation:
for my $i (0 .. $#lines1)
Which will iterate over the range 0 to the max index of the array.
Perl allows you to open filehandles on strings by using a reference to the scalar variable that holds the string:
open my $string1_fh, '<', \$string1 or die '...';
open my $string2_fh, '<', \$string2 or die '...';
while( my $line1 = <$string1_fh> ) {
my $line2 = <$string2_fh>;
....
}
But, depending on what you mean by difference (does that include insertion or deletion of lines?), you might want something different.
There are several modules on CPAN that you can inspect for ideas, such as Test::LongString or Algorithm::Diff.
my #lines1 = split(/^/, $str1);
my #lines2 = split(/^/, $str2);
# splits at start of line
# use /\n/ if you want to ignore newline and trailing spaces
for ($i=0; $i < #lines1; $i++) {
print "difference in line $i \n" if (lines1[$i] ne lines2[$i]);
}
Comparing Arrays is a way easier if you create a Hashmap out of it...
#Searching the difference
#isect = ();
#diff = ();
%count = ();
foreach $item ( #array1, #array2 ) { $count{$item}++; }
foreach $item ( keys %count ) {
if ( $count{$item} == 2 ) {
push #isect, $item;
}
else {
push #diff, $item;
}
}
#Output
print "Different= #diff\n\n";
print "\nA Array = #array1\n";
print "\nB Array = #array2\n";
print "\nIntersect Array = #isect\n";
Even after spliting you could compare them as Array.

How to print 'AND' between array elements?

If I have an array with name like below.
How do I print "Hi joe and jack and john"?
The algorithm should also work, when there is only one name in the array.
#!/usr/bin/perl
use warnings;
use strict;
my #a = qw /joe jack john/;
my $mesg = "Hi ";
foreach my $name (#a) {
if ($#a == 0) {
$mesg .= $name;
} else {
$mesg .= " and " . $name;
}
}
print $mesg;
Usually we use an array join method to accomplish this. Here pseudo code:
#array = qw[name1 name2 name2];
print "Hey ", join(" and ", #array), ".";
Untested:
{ local $, = " and "; print "Hi "; print #a; }
Just use the special variable $".
$"="and"; #" (this last double quote is to help the syntax coloring)
$mesg="Hi #a";
To collect the perldoc perlvar answers, you may do one of (at least) two things.
1) Set $" (list separator):
When an array or an array slice is interpolated into a double-quoted
string or a similar context such as /.../ , its elements are separated
by this value. Default is a space. For example, this:
print "The array is: #array\n";
is equivalent to this:
print "The array is: " . join($", #array) . "\n";
=> $" affects the behavior of the interpolation of the array into a string
2) Set $, (output field separator):
The output field separator for the print operator. If defined, this
value is printed between each of print's arguments. Default is undef.
Mnemonic: what is printed when there is a "," in your print statement.
=> $, affects the behavior of the print statement.
Either will work, and either may be used with local to set the value of the special variable only within an enclosing scope. I guess the difference is that with $" you are not limited to the print command:
my #z = qw/ a b c /;
local $" = " and ";
my $line = "#z";
print $line;
here the "magic" happens on the 3rd line not at the print command.
In truth though, using join is the most readable, and unless you use a small enclosing block, a future reader might not notice the setting of a magic variable (say nearer the top) and never see that the behavior is not what is expected vs normal performance. I would save these tricks for small one-offs and one-liners and use the readable join for production code.

Resources