convert string to logical expression - perl

I use configuration file to load logical expression which will later be replace with 0 or 1 depend on the success or failure
configuration
expression=(server.ip.1&&server.ip.2)||(server.ip.3&&server.ip.4)
Later server.ip.1 will replace with 1 or 0 depend on server availability. Later part of the logical expression evaluation has below piece of code.
my $reg_expression= configurator->get_reg_expression() ;
...
$reg_expression =~ s/$values[0]/$ip_status/g;
if ($reg_expression){
$logger->debug("no failover");
}else{
$logger->debug("falling to failover mode");
}
Issue is if condition always true what ever values assign to the expression. Issue seems to be it is taken as string so it always end up in true. Any other way that I can do the same or how can transfer above variable back to state where I can successfully use inside if condition.
$reg_expression = (0&&0)||(1&&0); # diffe
$reg_expression = '(0&&0)||(1&&0)';

You can use eval to evaluate the expression:
my $reg_expression = '(0&&0)||(1&&0)';
print eval $reg_expression, "\n";

Related

Perl `defined`function complexity

I have a perl script, which is accepting a list of arguments(mandatory and non-mandatory). Based on these args flow of script is determined. Where i am confused is, if the arg(which is in the form of switch) is used multiple times in the script to determine flow(most of the time used in if loop), then which one is better
if(defined arg){}
OR
my $switch = defined arg ? 1:0; if($switch){}
defined already returns true or false. Using the result of defined to select a true value for true case and a false value for the false case is just extra work. This does all the work you need and no extra work:
if( defined $foo ) { ... }
Sometimes I want to print that true or false value, so I'll use the double bang (negate, and negate again) to normalize the value:
!! defined $foo

What do Perl functions that return Boolean actually return

The Perl defined function (and many others) returns "a Boolean value".
Given Perl doesn't actually have a Boolean type (and uses values like 1 for true, and 0 or undef for false) does the Perl language specify exactly what is returned for a Boolean values? For example, would defined(undef) return 0 or undef, and is it subject to change?
In almost all cases (i.e. unless there's a reason to do otherwise), Perl returns one of two statically allocated scalars: &PL_sv_yes (for true) and &PL_sv_no (for false). This is them in detail:
>perl -MDevel::Peek -e"Dump 1==1"
SV = PVNV(0x749be4) at 0x3180b8
REFCNT = 2147483644
FLAGS = (PADTMP,IOK,NOK,POK,READONLY,pIOK,pNOK,pPOK)
IV = 1
NV = 1
PV = 0x742dfc "1"\0
CUR = 1
LEN = 12
>perl -MDevel::Peek -e"Dump 1==0"
SV = PVNV(0x7e9bcc) at 0x4980a8
REFCNT = 2147483647
FLAGS = (PADTMP,IOK,NOK,POK,READONLY,pIOK,pNOK,pPOK)
IV = 0
NV = 0
PV = 0x7e3f0c ""\0
CUR = 0
LEN = 12
yes is a triple var (IOK, NOK and POK). It contains a signed integer (IV) equal to 1, a floating point number (NV) equal to 1, and a string (PV) equal to 1.
no is also a triple var (IOK, NOK and POK). It contains a signed integer (IV) equal to 0, a floating point number (NV) equal to 0, and an empty string (PV). This means it stringifies to the empty string, and it numifies to 0. It is neither equivalent to an empty string
>perl -wE"say 0+(1==0);"
0
>perl -wE"say 0+'';"
Argument "" isn't numeric in addition (+) at -e line 1.
0
nor to 0
>perl -wE"say ''.(1==0);"
>perl -wE"say ''.0;"
0
There's no guarantee that this will always remain the case. And there's no reason to rely on this. If you need specific values, you can use something like
my $formatted = $result ? '1' : '0';
They return a special false value that is "" in string context but 0 in numeric context (without a non-numeric warning). The true value isn't so special, since it's 1 in either context. defined() does not return undef.
(You can create similar values yourself with e.g. Scalar::Util::dualvar(0,"").)
Since that's the official man page I'd say that its exact return value is not specified. If the Perl documentation talks about a Boolean value then then it almost always talks about evaluating said value in a Boolean context: if (defined ...) or print while <> etc. In such contexts several values evaluate to a false: 0, undef, "" (empty strings), even strings equalling "0".
All other values evaluate to true in a Boolean context, including the infamous example "0 but true".
As the documentation is that vague I would not ever rely on defined() returning any specific value for the undefined case. However, you'll always be OK if you simply use defined() in a Boolean context without comparing it to a specific value.
OK: print "yes\n" if defined($var)
Not portable/future proof: print "yes\n" if defined($var) eq '' or something similar
It probably won't ever change, but perl does not specify the exact boolean value that defined(...) returns.
When using Boolean values good code should not depend on the actual value used for true and false.
Example:
# not so great code:
my $bool = 0; #
...
if (some condition) {
$bool = 1;
}
if ($bool == 1) { ... }
# better code:
my $bool; # default value is undef which is false
$bool = some condition;
if ($bool) { ... }
99.9% of the time there is no reason to care about the value used for the boolean.
That said, there are some cases when it is better to use an explicit 0 or 1 instead of the boolean-ness of a value. Example:
sub foo {
my $object = shift;
...
my $bool = $object;
...
return $bool;
}
the intent being that foo() is called with either a reference or undef and should return false if $object is not defined. The problem is that if $object is defined foo() will return the object itself and thus create another reference to the object, and this may interfere with its garbage collection. So here it would be better to use an explicit boolean value here, i.e.:
my $bool = $object ? 1 : 0;
So be careful about using a reference itself to represent its truthiness (i.e. its defined-ness) because of the potential for creating unwanted references to the reference.

While using LWP:UserAgent, $response->is_success returns blank

I am using LWP::UserAgent like below
my $ua = LWP::UserAgent->new;
my $response = $ua->request ( ...);
if ( $response->is_success() )
{
...
}
print $response->is_success();
Problem I am facing is that is_success() is returning blank. I was expecting 1 (TRUE) or 0 (FALSE). What am I doing wrong here? Is print statement right?
Not returning anything is correct and usual way in Perl to return false result from function, don't expect literal 0 number when you only need logical false result. Your request is most likely returned with non 2xx or 3xx code.
From the Perl documentation:
The number 0, the strings '0' and "" , the empty list () , and undef
are all false in a boolean context. All other values are true.
Negation of a true value by ! or not returns a special false value.
When evaluated as a string it is treated as "" , but as a number, it
is treated as 0. Most Perl operators that return true or false behave
this way.
In other words, your mistake is assuming that boolean false is always represented by 0. It would be more accurate to say that in Perl false is represented by "empty", with what that means depending on the context.
This is useful, because it allows for clean code in a variety of contexts:
#evaluates false when there are no more lines in the file to process
while (<FILE>) { ... }
#evaluates false when there are no array elements
if (!#array) { ... }
#evaluates false if this variable in your code (being used as a reference)
#hasn't been pointed to anything yet.
unless ($my_reference) { ... }
And so on...
In your case, it's not clear why you want false to be equal to zero. The if() statement in your code should work as written. If you need the result to be explicitly numeric for some reason, you could do something like this:
my $numeric_true_false = ($response->is_success() ? 1 : 0);
From the discussion in comments:
$response->status_line
actually returned 500 Can't Connect to database.
With $response->is_success(), I was unable to understand the response from db.
Used $response->status_line to find out exactly where the code was failing.

coffeescript not implicitly returning "false"

I am trying to add a method to the String primitive type/class where I can extra params from a URL string
String::getUrlParams = -> # line 1
false unless ( params = #.split('?')[1] ) # line 2
# ...
In Chrome console, when I deliberately call this method with a string of URL without params, I expect it to just return false.
"http://dns.com/".getUrlParams();
but it goes pass through line 2.
if I change line 2 to
return false unless ( params = #.split('?')[1] ) # line 2`
then it does return false and stops the function at line 2
Any idea why coffeescript isn't returning false and halts the function in the first version?
Thank you
Coffeescript returns only the last function statement. If something follows some statement, that you want to return in middle of function, then you should do that explicitly.
--Short thoughts--
In short - Coffeescript compiler is not that smart, to predict, where you may want or may not want to return something. And same applies to most compilers now days. Also it's non-smartiness avoids most of mistakes, which would be caused because of premature return.

How can I set a default value for a Perl variable?

I am completely new to Perl. I needed to use an external module HTTP::BrowserDetect. I was testing some code and tried to get the name of the OS from os_string method. So, I simply initialized the object and created a variable to store the value returned.
my $ua = HTTP::BrowserDetect->new($user_agent);
my $os_name = $ua->os_string();
print "$user_agent $os_name\n";
there are some user agents that are not browser user agents so they won't get any value from os_string. I am getting an error Use of uninitialized value $os_name in concatenation (.) or string
How do I handle such cases when the $os_name is not initialized because the method os_string returns undef (this is what I think happens from reading the module source code). I guess there should be a way to give a default string, e.g. No OS in these cases.
Please note: the original answer's approach ( $var = EXPRESSION || $default_value ) would produce the default value for ANY "Perl false values" returned from the expression, e.g. if the expression is an empty string, you will use the default value instead.
In your particular example it's probably the right thing to do (OS name should not be an empty string), but in general case, it can be a bug, if what you actually wanted was to only use the default value instead of undef.
If you only want to avoid undef values, but not empty string or zeros, you can do:
Perl 5.10 and later: use the "defined-or operator" (//):
my $os_name = $ua->os_string() // 'No OS';
Perl 5.8 and earlier: use a conditional operator because defined-or was not yet available:
my $os_string = $ua->os_string(); # Cache the value to optimize a bit
my $os_name = (defined $os_string) ? $os_string : 'No OS';
# ... or, un-optimized version (sometimes more readable but rarely)
my $os_name = (defined $ua->os_string()) ? $ua->os_string() : 'No OS';
A lot more in-depth look at the topic (including details about //) are in brian d foy's post here: http://www.effectiveperlprogramming.com/2010/10/set-default-values-with-the-defined-or-operator/
my $os_name = $ua->os_string() || 'No OS';
If $ua->os_string() is falsy (ie: undef, zero, or the empty string), then the second part of the || expression will be evaluated (and will be the value of the expression).
Are you looking for defined?