Perl One liner for 3 conditions - perl

I have this
if($x<10){
print "child";
}elseif($x>10 && $x<18){
print "teenage"
}else{
print "old"
}
I want to put in a perl one liner how could i do this please help me

You may use the conditional operator. You also need only say print once - and I'm also going to change your conditions around, because 10 is neither >10 nor <10, but your code thinks 10 is old.
print $x<10 ? 'child' : $x<18 ? 'teenage' : 'old';

Conditional operator in Perl
You're looking for the conditional operator (a form of ternary operator which acts as a shorthand if-statement, not Perl-specific):
print $age < 10 ? "child" : $age < 18 ? "teenage" : "old";
Also, your code treats 10 as old, as it's neither less than nor greater than 10, so I've switched the function to what I think you wanted it to do.
Reusing the code
You can turn this into a subroutine for easy reuse:
sub determineAgeGroup {
my $age = $_[0];
return $age < 10 ? "a child" : $age < 18 ? "a teenager" : "old";
}
my #ages = (5,10,15,20);
foreach my $age (#ages) {
print "If you're $age you're " . determineAgeGroup($age) . "\n";
}
Output to this is:
If you're 5 you're a child
If you're 10 you're a teenager
If you're 15 you're a teenager
If you're 20 you're old
Link to working demo.

for my $x ( 5, 15, 55 ) {
print "$x is ";
print (($x<10) ? 'child' : ($x>10 && $x<18) ? 'teenage' : 'old');
print "\n";
}

No idea why you'd want to but this should work:
print (($x<10)?("child"):(($x>10 && $x<18)?("teenage"):("old")))
But just because it's short doesn't mean it's better than the original -- compare the difficulty in supporting/debugging the two options.
If you're just playing around the you could also define the strings in an appropriate array and do some maths on the value of $x to get a valid array entry.

Related

Remove "duplicate" formulas

I made a program that will take in 5 numbers and will use the first four to acquire the fifth number as a solution. Solutions can only contain positive integers, and the only operators acceptable are "+ - * /". There are 11 different way that the numbers and operators can be arranged with parentheses. Ex "(n # n) # n # n" where n represents numbers and # represents operators.
I have no problem finding all the solutions, my problem is removing "duplicates". I have been able to remove most duplicates using
%Seen = ();
#solutions = grep { ! $Seen{ $_ }++ } #solutions;
However I am unable to figure out a way to remove "duplicate" formulas.
Using 21 14 2 7 to acquire 34 gives us 4 solutions after the first duplicates have been removed. Here they are
21/7=3; 14+3=17; 2*17=34
21/7=3; 3+14=17; 2*17=34
21/7=3; 3+14=17; 17*2=34
21/7=3; 14+3=17; 17*2=34
My teacher considers these mathematically the same and as such all four of them are just 1 solution. What I can't figure out how to do is find these "duplicates" and remove them. Any help is appreciated, thank you.
A more generic form of the dedupping code you used is
grep !$seen{key($_)}++, ...
In this case, key would be
sub key {
( my $key = $_[0] ) =~ s/(\d+)([*+])(\d+)/ $1 < $3 ? "$1$2$3" : "$3$2$1" /eg;
return $key;
}
In your case, you might want to simply normalise your inputs first
sub normalise(_) {
( my $s = $_[0] ) =~ s/(\d+)([*+])(\d+)/ $1 < $3 ? "$1$2$3" : "$3$2$1" /eg;
return $s;
}
#solutions = grep !$seen{$_}++, map normalise, #solutions;
For example, for commutative operations, only consider x # y where x <= y. This way, 2 * 17 is possible, but 17 * 2 is not.

Reversing the Operand of Smart Matching operator doesn't give same result

I am facing a strange problem here in the working of Smart Matching Operator..
I have read that the order of operand while using the Smart Matching Operator(~~) doesn't matter, and it gives the same result.. But in an example which I have shown below, this doesn't work..
I want to check whether an element is amongst one of the elements of a given array or not..
Below are the two ways I tried : -
First way: - ($result ~~ #nums)
#!/perl/bin
use v5.14;
my #nums = qw( 1 2 3 27 42 );
my $result = 27;
say "The result [$result] is one of the input values (#nums)" if $result ~~ #nums;
Second way: - (#nums ~~ $result)
#!/perl/bin
use v5.14;
my #nums = qw( 1 2 3 27 42 );
my $result = 27;
say "The result [$result] is one of the input values (#nums)" \
if #nums ~~ $result;
However, the first way is working fine, and it is printing the statement, but in the second way, it is not printing..
i.e. : - #nums ~~ $result is not giving the same result as $result ~~ #nums
I can't understand why this is happening..
Can anyone help me. I am unable to find this problem on SO.
according to to the perlop, it says (among many other stuff):
It is often best read aloud as "in", "inside of", or "is contained
in", because the left operand is often looked for inside the right
operand.
you may want to look at the table there, especially on the following sections:
Left Right Description and pseudocode
===============================================================
Any ARRAY smartmatch each ARRAY element[3]
like: grep { Any ~~ $_ } ARRAY
Any Num numeric equality
like: Any == Num
and remember that if Any is array and Num is a scalar, the following are equal:
Any == Num <=> scalar(Any) == Num

Perl ternary errantly enters "else" clause?

I have the following code:
# List of tests
my $tests = [("system_test_builtins_sin", "system_test_builtins_cos", "system_test_builtins_tan")];
# Provide overrides for certain variables that may be needed because of special cases
# For example, cos must be executed 100 times and sin only 5 times.
my %testOverrides = (
system_test_builtins_sin => {
reps => 5,
},
system_test_builtins_cos => {
reps => 100,
},
);
my %testDefaults = (
system_test_reps => 10,
);
# Execute a system tests
foreach my $testName (#$tests)
{
print "Executing $testName\n";
my $reps;
if (exists $testOverrides{$testName}{reps})
{ $reps = $testOverrides{$testName}{reps}; }
else
{ $reps = $testDefaults{system_test_reps}; }
print "After long if: $reps\n";
exists $testOverrides{$testName}{reps} ? $reps = $testOverrides{$testName}{reps} : $reps = $testDefaults{system_test_reps};
print "After first ternary: $reps\n";
exists $testOverrides{$testName}{reps} ? $reps = $testOverrides{$testName}{reps} : print "Override not found.\n";
print "After second ternary: $reps\n";
}
This gives the following output:
Executing system_test_builtins_sin
After long if: 5
After first ternary: 10
After second ternary: 5
Executing system_test_builtins_cos
After long if: 100
After first ternary: 10
After second ternary: 100
Executing system_test_builtins_tan
After long if: 10
After first ternary: 10
Override not found.
After second ternary: 10
This output is most unexpected! I don't understand why the first ternary seems to always be executing the "if false" clause. It is always assigning a value of 10. I also tried changing the "false" clause to $reps = 6, and I saw that it always got the value of 6. Why does the ternary's logic depend on the content of the third (if false) clause?
Here's a simpler script that illustrates the problem:
#!/usr/bin/perl
use strict;
use warnings;
my $x;
1 ? $x = 1 : $x = 0;
print "Without parentheses, \$x = $x\n";
1 ? ($x = 1) : ($x = 0);
print "With parentheses, \$x = $x\n";
It produces this output:
Without parentheses, $x = 0
With parentheses, $x = 1
I'm not sure that the relationship between assignment and ?: can be complete explained by operator precedence. (For example, I believe C and C++ can behave differently in some cases.)
Run perldoc perlop and search for "Conditional Operator", or look here; it covers this exact issue (more concisely than I did here).
In any case, I think that using an if/else statement would be clearer than using the ?: operator. Or, since both the "true" and "false" branches assign to the same variable, a better use of ?: would be to change this:
exists $testOverrides{$testName}{reps}
? $reps = $testOverrides{$testName}{reps}
: $reps = $testDefaults{system_test_reps};
to this:
$reps = ( exists $testOverrides{$testName}{reps}
? testOverrides{$testName}{reps}
: $testDefaults{system_test_reps} );
But again, the fact that I had to wrap the line to avoid scrolling is a good indication that an if/else would be clearer.
You might also consider using the // operator, unless you're stuck with an ancient version of Perl that doesn't support it. (It was introduced by Perl 5.10.) It's also known as the "defined-or" operator. This:
$x // $y
is equivalent to
defined($x) ? $x : $y
So you could write:
$reps = $testOverrides{$testName}{reps} // $testDefaults{system_test_reps};
This doesn't have exactly the same semantics, since it tests the expression using defined rather than exists; it will behave differently if $testOverrides{$testName}{reps} exists but has the value undef.
The -p option to B::Deparse is illuminative for problems like this:
$ perl -MO=Deparse,-p -e '$condition ? $x = $value1 : $x = $value2'
(($condition ? ($x = $value1) : $x) = $value2);
As Keith Thompson points out, it is all about the precedence. If the condition is false, the ultimate assignment is $x = $value2. If the condition is true, then the assignment is ($x = $value1) = $value2 -- either way the outcome is to assign $value2 to $x.
I would do it this way (I dont mind using ternary operators)
$reps = exists($testOverrides{$testName}{reps}) ?
$testOverrides{$testName}{reps} :
$testDefaults{system_test_reps};
HTH
Thanks for giving us a sample of your code. Now we can tear it to pieces.
Don't use the ternary operator. It's an infection left over to originally make C programmers feel comfortable. In C, the ternary operator was used because it was originally more efficient than an if/else statement. However, compilers are pretty good about optimizing code, so that's no longer true and now it's discouraged in C and C++ programming. Programming in C is hard enough as it is without ternary operators mucking about.
The Perl compiler is also extremely efficient at optimizing your code, so you should always write for maximum clarity, so others who aren't as good as programming and get stuck maintaining your code can muddle through their job.
The problem you're having is one of operator precedence. You're assuming this:
(exists $testOverrides{$testName}{reps})
? ($reps = $testOverrides{$testName}{reps})
: ($reps = $testDefaults{system_test_reps});
I would too. After all, that's what I pretty much mean. However, the assignment operator has lower precedence than the ternary operator. What's really happening is this:
(exists $testOverrides{$testName}{reps})
? ($reps = $testOverrides{$testName}{reps}) : ($reps))
= $testDefaults{system_test_reps});
so, the final assignment is always happening to $reps.
It's much better if you use if/else:
if (exists $testOverrides{$testName}{reps}) {
$reps = = $testOverrides{$testName}{reps};
}
else {
$reps = $testDefaults{system_test_reps};
}
No precedence issues, easier to read, and just as efficient.

Perl: Finding _ followed by X with stuff in between

many thanks for the help with the earlier issues.
I've almost finished the last thing I was working on - specifically an ORF (open reading frame) finder program. So far, I've got an array called #AminoAcidArray1. All the start codons are "_" and all the stop codons are "X".
How do I count the ORFs? Put another way, How do I count times in the array when "_" is followed by "X" with random ignorable characters between? What sort of loop should I be using? I need a ~= there somewhere I think
And yes, I know bioPerl can do this easily, but only activePerl is available for some reason.
Sincerest thanks,
Shtanto
First, contemporary ActivePerl has Bundle::BioPerl
in its main 'Activeperl' repository. This should allow
a BioPerl installation on some ActivePerl versions.
Then,
print "$-[0]..$+[0]\n" while $orf =~ /_[^X]*X/g;
prints start- (_) and stop (X) index of your orfs contained in
$orf if they are consecutive (not nested). If nested, then you'd have
to use slightly more complicated expressions (with recursion).
BTW.: What does the expression
print join ',', #AminoAcidArray1;
print on your console?
rbo
If I understand it right from your comment:
you have an array, you don't need =~ operator.
You need to traverse the array once and remember the current state of what you call "reading window". Say:
my $state = 0;
my $count = 0;
for my $item (#array) {
if ($item eq "_") {
if ($state==0) {
$state=1;
}
} elsif ($item eq "X") {
if ($state==1) {
$state=0;
$count++;
}
}
}
return $count;
Your question is too specific to your domain, but what I understand is that you want to count some occurrences in an array, this is what I does in the following code (I use perlconsole) :
Perl> my #a = qw/az ae ar at ay au pr lf/
8
Perl> my $count = grep /^a/, #a
6
Perl> print "$count\n"
6
1
Perl>

Is there a way to test if a scalar has been stringified or not?

I am writing a thing to output something similar to JSON, from a perl structure. I want the quoting to behave like this:
"string" outputs "string"
"05" outputs "05"
"5" outputs "5"
5 outputs 5
05 outputs 5, or 05 would be acceptable
JSON::XS handles this by testing if a scalar has been "stringified" or not, which I think is very cool. But I can't find a way to do this test myself without writing XS, which I'd rather avoid. Is this possible? I can't find this anywhere on CPAN without finding vast pedantry about Scalar::Util::looks_like_number, etc which completely isn't what I want. The only stopgap I can find is Devel::Peek, which feels evil. And also, just like JSON::XS, I'm fine with this secenario:
my $a = 5;
print $a."\n";
# now $a outputs "5" instead of 5)
Inspect the output of B::svref_2object:
use B;
($x, $y, $z) = ("5", 5, 5.0);
print ref(B::svref_2object( \$x )), "\n";
print ref(B::svref_2object( \$y )), "\n";
print ref(B::svref_2object( \$z )), "\n";
Output:
B::PV
B::IV
B::NV
Or, as ikegami suggests, if you'd rather lookup the pPOK flag:
if (B::svref_2object( \$x )->FLAGS & B::SVp_POK) {
print "I guess \$x is stringy\n";
}
You can make it so a number no longer appears to have been stringified by using the following:
$x = 0+$x;
For example,
$ perl -MJSON::XS -E'
$_ = 4;
say encode_json([$_]); # [4]
"".$_;
say encode_json([$_]); # ["4"]
$_ = 0 + $_;
say encode_json([$_]); # [4]
'
Detecting whether something has been stringified is tougher because JSON::XS is looking into Perl internals. One could use the following:
sub is_stringy {
{ no warnings 'void'; "".$_[0]; }
return 1;
}
but I don't think that's what you want :) I don't know how to detect the "corruption" without writing some XS code. What you want to know is if SvPOKp is true for the scalar (after you call SvGETMAGIC on the scalar).
use Inline C => <<'__EOI__';
SV* is_stringy(SV* sv) {
SvGETMAGIC(sv);
return SvPOKp(sv) ? &PL_sv_yes : &PL_sv_no;
}
__EOI__
$_ = 4;
say is_stringy($_) ?1:0; # 0
{ no warnings 'void'; "".$_; }
say is_stringy($_) ?1:0; # 1
$_ = 0+$_;
say is_stringy($_) ?1:0; # 0
oo! It turns out that B does provide SVp_POK, so it can (almost) be done in without writing new XS code
use B qw( svref_2object SVp_POK );
sub is_stringy {
my ($s) = #_;
my $sv = svref_2object(\$s);
#$sv->GETMAGIC(); # Not available
return $sv->FLAGS & SVp_POK;
}
Being unable to call SvGETMAGIC has drawbacks, but it will work almost all of the time.
This is probably not the best way, but if JSON does what you want, why not use it?
sub is_stringy {
encode_json([$_[0]]) =~ /["']/
}
is_stringy(5); # undef
is_string("5"); # 1