perl ternary statement has odd results with print - perl

I've been messing around with writing FizzBuzz in perl...to be specific I want to make a nightmare looking line of code just to see if I can do it. This means nested ternary statements of course!
However I'm finding that on the 15's it never prints FizzBuzz, just Fizz. I cannot find a reason because that implies that if the first ternary statement returns true it's just skipping the second statement.
Here's the little nightmare I've come up with. Yes it can be way worse, but I'm really not that strong with perl and this is just an exercise for myself:
#!/usr/bin/perl
for (my $i=1; $i<=100; $i++) {
( !( ($i % 3 == 0) ? print "Fizz" : 0) && !( ($i % 5 == 0) ? print "Buzz" : 0) ) ? print "$i\n" : print "\n";
}
Here's the first 20 lines of output:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz
16
17
Fizz
19
Buzz
What would the print statement be doing that would cause this to happen?

This is an example of short-circuit evaluation, as documented in perlop. In cases of a() && b(), b() will never be evaluated if a() is false. In your (deeply nested and confusing) ternary statement, that amounts to the same thing. To fix this I'd split the statement up into multiple lines.

Simplified:
for my $i ( 1 .. 20 ) {
print +( ( $i % 3 ? '' : 'Fizz' ) . ( $i % 5 ? '' : 'Buzz' ) ) || $i, "\n";
}
Outputs:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

Actually, here we go, bit shifting saves the day
#!/usr/bin/perl
for (my $i=1; $i<=100; $i++) {
(((($i % 3 == 0) ? print "Fizz" : 0) + ( ($i % 5 == 0) ? print "Buzz" : 0)) << 1) ? print "\n" : print "$i\n";
}
Output:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
The most klugey solution

Related

Perl Range Operator with Letters and Numbers

Is there a way to use Perl's range operator .. to use letters AND numbers?
For example, have:
for my $i ('X'..'9') {
print "$i ";
}
output X Y Z 1 2 3 4 5 6 7 8 9
Not unless for my $i ('X' .. 'Z', 1 .. 9) counts. "Z" will never increment to 1.

How can I square a number in Perl?

I have code to print out the first 9 squared numbers:
#!/usr/local/bin/perl
for($i=1;$i<10 ;$i++ )
{
printf $i^2 . "\n";
}
but for some reason this just outputs 30167451011. How do I properly square a number?
To square you have to use $i**2
#!/usr/local/bin/perl
for ( my $i = 1; $i < 10; $i++ ) {
print $i**2 . "\n";
}
This will output:
1
4
9
16
25
36
49
64
81
To explain what happened in the original code, you need to know 3 things: first, ^ is the XOR operator in perl, because it is the XOR operator in C.
1 ^ 2 = 3
2 ^ 2 = 0
3 ^ 2 = 1
4 ^ 2 = 6
...
Second, the ^ operator has lower precedence than the string concatenation operator . so $i^2 . "\n" is equivalent to $i ^ (2 . "\n")
Third, perl converts between strings and numbers as necessary. The . operator requires strings on both sides, so the 2 is converted to "2" and concatenated with the "\n" to become the string "2\n".
Then the ^ operator requires numbers on both sides, so the string "2\n" is converted to a number - by taking the leading number-looking portion and throwing away the rest. So the result of $i ^ 2 . "\n" is ultimately the same as $i ^ 2. Your "\n" didn't have any effect at all, so all the results are printed with nothing between them. 3, 0, 1, 6, ... became 3016...
^ is the Bitwise Xor operator
To square a number, you want the Exponentiation operator **
for my $i ( 1 .. 9 ) {
print $i**2, "\n";
}
Outputs:
1
4
9
16
25
36
49
64
81
foreach my $i (1..9){
say $i**2;
}
It can be achieve by doing this way also:
for (1..9) {
print $_*$_,"\n"
}
Output:
1
4
9
16
25
36
49
64
81
You can use the code below to get the squares.
print $_**2,"\n" for 1..10;

Condition in ternary operator doesn't cause any change

Here is my code sample:
for $i(1..100){
if ($i%15==0){$s="Divisible by 15"}
elsif($i%5==0){$s="Divisible by 5"}
else {$i%3==0 ? $s="Divisible by 3" : $s=$i};
print $s."\n"}
This displays partial correct results, if a number if divisible by 3, it displays the "number" and not "Divisible by 3".
Example of output:
1
2
3
4
Divisible by 5
6
7
8
9
Divisible by 5
PS: I have to write this code in the minimum no. of characters possible. (The reason why the code is so sluggish)
The fourth line is parsed as
(((($i % 3) == 0) ? ($s = 'Divisible by 3') : $s) = $i)
meaning that even when $i is divisible by 3, the assignment looks like
($s = 'Divisible by 3') = $i
Some fixes:
$i%3==0 ? ($s="Divisible by 3") : ($s=$i)
$s = ($i%3==0 ? "Divisible by 3" : $i)
Pro-tip: B::Deparse is very helpful for figuring this stuff out. I ran the command
perl -MO=Deparse,-p -e '$i%3==0 ? $s="Divisible by 3" : $s=$i'
to see exactly what the problem was.
The ternary ?: has higher precedence than assignment. Use parens to sort that out:
for my $i (1 .. 100) {
my $s = "Divisible by ";
$i%15==0 ? ($s .= 15)
: $i%5==0 ? ($s .= 5)
: $i%3==0 ? ($s .= 3)
: ($s =$i);
print "$s\n";
}
Output:
1
2
Divisible by 3
4
Divisible by 5
Divisible by 3
7
8
Divisible by 3
Divisible by 5
If you want a version with less characters,
for $i(1..100){print(($i%3&&$i%5?$i:"Divisible by ".($i%5?3:$i%15?5:15))."\n");}
Parenthesis after the print function means call the print function, not group the expression. Caused by the fact that parenthesis are optional for that function.

Manipulating digits

This is a program which grabs lines which contains the $position AND $amino value in the first two columns.
Code:
#!/usr/bin/perl
my $id = $ARGV[0];
my $position = $ARGV[1]; # POSITION OF THE RESIDUE
my $amino= $ARGV[2]; #THREE LETTER AMINO ACID CODE IN CAPITALS
my #grabbed;
open (FILE, $id.$amino.$position.".hb2");
#CREATES AN ARRAY WITH ONLY THE VALUES FROM THE HB2 FILE. REMOVES THE HEADER OF THE FILE.
while (<FILE>) {
if (/^-/) {
push #grabbed, $_;
while (<FILE>) {
last if /^$/;
push #grabbed, $_;
}
}
}
close (FILE);
for ( #grabbed ) {
my #f = split;
if (( $f[2] == "-"."00".$position."-".$amino ) or ($f[0] == "-"."00".$position."-".$amino)) {
push #line, $id.$amino.$position, " ",$_;
}
}
print #line;
Partial input data :
-0007-ARG NH2 -0009-GLN OE1 3.24 SS 2 6.00 143.3 2.38 105.9 95.8 1 #CASE 1
-0008-GLU N -0008-GLU OE1 2.62 MS 0 -1.00 120.8 1.96 102.3 103.4 2
-0011-ILE N -0117-ARG O 2.87 MM 106 4.90 144.0 2.00 127.5 139.0 3
-0117-ARG N -0011-ILE O 2.75 MM 106 4.90 160.4 1.79 153.2 148.6 4 #CASE 2
-0016-SER N -0012-THR O 2.89 MM 4 6.00 156.2 1.95 149.8 154.8 5 #CASE 3
-0017-ALA N -0013-LEU O 3.10 MM 4 6.24 152.8 2.17 143.4 149.7 6
-0018-GLU N -0014-ARG O 3.04 MM 4 6.24 154.1 2.11 147.2 154.2 7
-0019-ILE N -0015-GLY O 2.90 MM 4 6.16 155.8 1.96 150.7 156.2 8
-0016-SER OG -0188-THR OG1 2.72 SS 172 5.92 172.0 1.73 98.9 99.6 9
-0188-THR OG1 -0016-SER OG 2.72 SS 172 5.92 163.7 1.75 116.4 115.1 10
Question :
In order to generalize the program I made the match as :
( $f[2] == "-"."00".$position."-".$amino ) or ($f[0] == "-"."00".$position."-".$amino)
The format is always four digits after "-" before $amino (-0188-THR). I suddenly realized that my code wouldnt work if the $position input is "one digit(like CASE 1)" or "three digit (like CASE 2, column 1)". Since I hard coded it as format as "-" followed by two zeros and THEN position, it has to always be two digit input to work.
I am stumped to generalize this code so that I could put in 1/2/3 digits. The remaining digits would always be replaced by zeros.
You can format the string using sprintf:
my $mstring = sprintf("-%04d-%s", $position, $amino);
if ( ($f[2] eq $mstring) or ($f[0] eq $mstring) ) {
# ...
}
Here, %04d adds 0's to the left of position to make it 4 digits long.
First, == operator in perl used only for comparing arithmetic expressions
To compare strings you should use eq operator
Second, to format strings from digits you can use sprintf function.
if ($f[2] eq "-".sprintf("%04d", $position)."-".$amino ...

print the number which is present in documentA but absent in documentB

I've a problem in making a Perl program for matching the numbers in two documents. Let's say there are documents A and B.
So I want to have the numbers which is present in document A and absent in the document B.
Example 1:
DocA: 1 2 3 5 6 8 9 10 11 12 13
DocB: 1 2 3 6 7 8 9 10 11
output:
5 12 13
EDITED:
#a=qw( 1 2 3 5 6 8 9 10 11 12 13);
#b=qw( 1 2 3 4 5 6 7 8 9 10 11);
#new=();
#new1=();
for($i=0;$i<=$#a;$i++)
{
for($j=0;$j<=$#b;$j++)
{
if($a[$i] ne $b[$j])
{
push(#new,$b[$j]);
}
}
}
You could use the CPAN module Array::Utils. The following will do what you need:
use Array::Utils qw(:all);
my #a = qw( 1 2 3 5 6 8 9 10 11 12 13);
my #b = qw( 1 2 3 4 5 6 7 8 9 10 11);
my #diff = array_minus(#a, #b);`
By the way, the reason your program does not work is because you made a logic error. You are adding a value to #new EVERY TIME the value does not match. So, in the first iteration of the loop you compare the a value to the b value. Even though the value is equal to the first element of #b, it is not equal to the other ten elements of #b and hence all of these elements are added to #new. I have rewritten your loop. Note the more Perlish loops instead of the C-loop you used in your code.
my #a = qw( 1 2 3 5 6 8 9 10 11 12 13);
my #b = qw( 1 2 3 4 6 7 8 9 10 11);
my #new = ();
for my $a_value (#a) {
my $b_not_in_a = 1;
INNER: for my $b_value (#b)
{ if($a_value == $b_value) {
$b_not_in_a = 0;
last INNER; }
}
if ($b_not_in_a)
{
push(#new,$a_value);
}
}
Consider Algorithm::Diff ?
You could use a hash. Read DocA and initialize a hash using the read numbers as keys:
open(INPUT, "DocA");
while (<INPUT>)
{
chomp;
$myhash{$_} = 1;
}
Then read DocB and foreach number, check if already defined in your hash:
open(INPUT, "DocB");
while (<INPUT>)
{
chomp;
if (not defined $myhash{$_})
{
print "$_\n";
}
}
This code asumes you have a number per line. If your files are formatted differently, you will need to adapt it.
This code will work even if your numbers aren't ordered.
The lists in your example are sorted. I am assuming that they are, and that you're not allowed to use modules since it is homework. Also, since it is homework, I won't give the answer, but some hints in the right direction.
If you would do this by hand, and you are only allowed to look at the front of each row, how would you do it? If the head of A is a number smaller than B, what does that mean? If it is equal, what does that mean? If it is larger, what does that mean?
Now you know how you can handle one situation, from that you can create some kind of step to reduce the problem. Now define when you need to stop, and what the possible leftovers of the lists are at that point, and how you can get your answer from the values you collected in the step, and the remainder after you stop.
Some examples of extreme cases:
#a = qw();
#b = qw(1 2 3);
#a = qw (1 2 3);
#b = qw (4 5 6);
#a = qw(1 3 5);
#b = qw(2 4 6)
Good luck!