Date validator using subroutine in perl - perl

I have dates say $date1, $date2, $date3.
I want to create array of these dates pass to subroutine & want to retrieve status of each date. Regular expression inside subroutine will evaluate date format.
I have create subroutine as DateValidator as,
my #newDateArray = qw /$date1, $date2, $date3/;
foreach (#newDateArray) {
print "Date used $_ : ";
DateValidator($_);
}
# Subroutine to evaluate dates
sub DateValidator {
my $dateVal=shift;
if ($dateVal =~ /^20?\d{2}\-0?(:?[1-9]|10|11|12)\-(\d{2})$/) {
if ($2 <= 31) {
print "All DD's are correct\n";
} else {
print "Please verify the DD again !\n";
}
} else {
print "Please enter correct date !\n";
}
}
This does not work as expected. Any help would be appreciated.

The qw() function does not interpolate variables. So this code:
my #newDateArray = qw /$date1, $date2, $date3/;
Needs to be:
my #newDateArray = ($date1, $date2, $date3);
I.e. replace qw() with a simple pair of parentheses.
This is not explicitly mentioned in the documentation, but there is a rather subtle mention:
Evaluates to a list of the words extracted out of STRING, using embedded whitespace as the word delimiters. It can be understood as being roughly equivalent to:
split(" ", q/STRING/);
Where the observant people will notice that a single quoted STRING -- using q() -- will not interpolate variables. This could have been written quite a few hundred times clearer, I agree.
Also, you might notice that the documentation says:
A common mistake is to try to separate the words with comma or to put comments into a multi-line qw-string. For this reason, the use warnings pragma and the -w switch (that is, the $^W variable) produces warnings if the STRING contains the "," or the "#" character.
Which you have not noticed, which makes me suspect that you are not using warnings. This is a very, very bad idea. See Why use strict and warnings? for more information.

Related

Perl - Subroutine argument is another subroutine call

I have a subroutine called grepText, which simply greps a text from another variable. I am trying to split the output. Is it possible to pass the output of grepText as an argument to split directly? without putting the value of grepText in a variable first ? grepText returns a string.
What i am trying to do is:
$output = (split ":", grepText("findThis", $Alltext))[1];
grepText is as follows
sub grepText(){
my #text = split "\n", $_[1];
my $output = grep /$_[0]/, #text;
return $output;
}
it doesn't work. Error is
Too many arguments for main::grepText at a line 115, near "$Alltext)"
It is very much possible to pass the input of a subroutine to any perl function directly without using a perl variable.
I think the issue might be with your "grepText" subroutine. To debug the issue in detail, much more information is required.
I did try your routine and I was able to get the required output:
#!/usr/bin/perl
sub grepText
{
return "hello:world"; # returns a test string
}
my $output = (split ":", grepText($textToFind, $Alltext))[1];
print "$output";
Output:
world
Sure it is. But as you've written it grepText is getting some strange parameters. In
(split ":", grepText(/$textToFind/, $Alltext))[1];
you're calling grepText(/$textToFind/, $Alltext) which is searching for the value of $textToFind in the global variable $_ and, in list context, is inserting either an empty list () or a list containing 1 (1) into the parameters
So you're calling grepText($Alltext) or grepText(1, $Alltext) depending on whether $_ contains the regex pattern in $textToFind
I'm pretty certain that's not what you want to do, so some more information would be nice!
However, whatever grepText returns will be split on colons : and (split ":", grepText(...))[1] will give you the second colon-separated field, which seems to be what you're asking

Perl trim spaces from scalar or array

This is a very basic Perl question but I just want to make sure the actual good practice to it.
Consider I have built a function to trim spaces from strings and I will pass to it either single scalar as string or array of strings, I have this basic working example:
sub trim_spaces {
my (#out) = #_;
for (#out) {
s/\s+//g;
}
return (scalar #out >1)? #out : $out[0];
}
this works in the following calls:
trim_spaces(" These Spaces Are All Removed");
and
#str = (" Str Number 1 ", " Str Number 2 ", " Str Number 3 ");
trim_spaces(#str);
What I am trying to do and understand is the shortest version of this function like this:
sub trim_spaces {
s/\s+//g for (#_);
return #_;
}
This works only if I pass an array:
trim_spaces(#str);
but it does not work if I pass a scalar string:
trim_spaces(" These Spaces Are All Removed");
I understand it should be converted from scalar ref to array, how this can be done in the short version.
Trying to understand the best practices of Perl.
The strict best practice answer to this is to always unpack the contents of #_ into lexical variables, first thing. Perl Best Practices provides the following (paraphrased) arguments:
It's not self-documenting to directly access #_. $_[0], $_[1], and so on tell you nothing about what these parameters are for.
The aliasing behavior of #_ is easily forgotten and can be a source of hard-to-find bugs in a program. Whenever possible, avoid spooky action at a distance.
You can verify each argument while unpacking the #_ array.
And one argument not from PBP:
Seeing my $self = shift; at the beginning of a subroutine clearly marks it as an OO method instead of an ordinary sub.
Sources: Perl Best Practices (Conway 2005), Perl::Critic's relevant policy from PBP.
The elements in #_ are aliases to the original values, which means modifying them inside the subroutine will change them outside as well. The array you're returning is ignored in your examples.
If you store the string in a variable this would work:
my $string = ' These Spaces Are Removed ';
trim_spaces($string); # $string is now 'TheseSpacesAreRemoved'
Or you could use non-destructive substitution and assign the results created by this:
sub trim_spaces { return map { s/\s+//gr } #_ }
my #trimmed = trim_spaces('string one', ' string two');
my ($trimmed_scalar) = trim_spaces('string three');
map will create a list of the values returned by the substitution with the /r flag. The parens around $trimmed_scalar are necessary; see the last example for a version where it isn't.
Alternatively, you could copy the parameters inside the subroutine into lexical variables to avoid action at a distance, which is generally better practice than directly modifying the elements of #_:
sub trim_spaces
{
my #strings = #_;
s/\s+//g for #strings;
return #strings;
}
Personally, I find it nicer when the subroutine returns a value without side effects, and the /r flag saves me the trouble of thinking of a better name for a lexical copy. We can use wantarray to make it smarter in regards to the calling context:
sub trim_spaces
{
return if not defined wantarray;
return map { s/\s+//gr } #_ if wantarray;
return shift =~ s/\s+//gr;
}
On a side note, trim_spaces would be better named remove_whitespace or something similar. Trimming usually means to remove leading and trailing whitespace, and the \s character class matches tabs, newlines, form feeds, and carriage returns in addition to spaces. Use tr/ //dcr to remove just spaces instead if that's what you wanted.

Why do I get a syntax error in my compound if statement?

Why do I get a syntax error in the following script?
print "Enter Sequence:";
$a = <STDIN>;
if ($a=="A")|| ($a== "T")|| ( $a == "C")|| ($a== "G")
{
print $a;
}
else
{
print "Error";
}
First, you have a syntax error: The condition expression of an if statement must be in parens.
The second error is found by using use strict; use warnings;, something you should always do. The error is the use of numerical comparison (==) where string comparison (eq) is called for.
The final problem is that $a will almost surely contain a string ending with a newline, so a chomp is in order.
The immediate problem is that he entire logical expression for an if must be in parentheses.
In addition
You must use eq instead of == for comparing strings
Your input string will have a trailing newline, so it will look like "C\n" and will not match a simple one-character string. You need to chomp the input before you compare it
It is generally better to read from STDIN using <> rather than <STDIN>. That way you can specify an input file on the command line, or read from the STDIN if no input was provided
You must always put use strict and use warnings at the top of your program. That will catch many simple errors that you may otherwise overlook
You shouldn't use $a as a variable name. It is a symbol reserved by Perl itself, and says nothing about the purpose of the variable
It is best to use a regular expression for simple comparisons like this. It makes your code much easier to read and will usually make the execution very much faster
Please take a look at this program, which I think does what you want.
use strict;
use warnings;
print "Enter Sequence: ";
my $input = <>;
chomp $input;
if ( $input =~ /^[ATCG]$/i ) {
print $input, "\n";
}
else {
print "Error";
}

Strange perl code - looking for explanation

I know a little bit perl, but not enough deeply to understand the next.
Reading perldelta 5.18 i found the next piece of code what is already disabled in 5.18. Not counting this, still want understand how it's works.
Here is the code and in the comments are what i understand
%_=(_,"Just another "); #initialize the %_ hash with key=>value _ => 'Just another'
$_="Perl hacker,\n"; #assign to the $_ variable with "Perl..."
s//_}->{_/e; # darkness. the /e - evauates the expression, but...
print
it prints:
Just another Perl hacker,
I tried, the perl -MO=Deparse and get the next
(%_) = ('_', 'Just another '); #initializing the %_ hash
$_ = "Perl hacker,\n"; # as above
s//%{'_';}/e; # substitute to the beginning of the $_ - WHAT?
print $_; # print the result
japh syntax OK
What is strange (at least for me) - running the "deparsed" code doesn't gives the original result and prints:
1/8Perl hacker,
I would be very happy:
if someone can explain the code, especially if someone could write an helper code, (with additional steps) what helps me understand how it is works - what happens.
explain, why the deparsed code not prints the original result.
What means the %{'_';} in the deparsed code?
The code actually executed by the substitution operator is probably actually something like
my $code = "do { $repl_expr }";
So when the replacement expression is _}->{_, the following is executed:
do { _}->{_ }
_ simply returns the string _ (since strict is off), so that's the same as
do { "_" }->{_}
which is the same as
"_"->{_}
What you have there is a hash element dereference, where the reference is a symbolic reference (i.e. a string rather than an actual reference). Normally forbidden by strict, here's an example of a symbolic reference at work:
%h1 = ( id => 123 );
%h2 = ( id => 456 );
print "h$_"->{id}, "\n"
for 1..2;
So that means
"_"->{_} # Run-time symbol lookup
is the same as
$_{_} # Compile-time symbol lookup
A similar trick is often used in one-liners.
perl -nle'$c += $_; END { print $c }'
can be shortened to
perl -nle'$c += $_; }{ print $c'
Because the code actually executed when -n is used is obtained from something equivalent to
my $code = "LINE: while (<>) { $program }";
%{'_';}
is a very weird way to write
%{'_'}
which is a hash dereference. Again, the reference here is a symbolic reference. It's equivalent to
%_
In scalar context, hash current returns a value that reports some information about that hash's internals (or a false value if empty). There's been a suggestion to change it to return the number of keys instead.

Perl: basic question, function functionality

What does this function do?
sub MyDigit {
return <<END;
0030\t0039
END
}
That's called a "here-document", and is used for breaking strings up over multiple lines as an alternative to concatenation or list operations:
print "this is ",
"one line when printed, ",
"because print takes multiple ",
"arguments and prints them all!\n";
print "however, you can also " .
"concatenate strings together " .
"and print them all as one string.\n";
print <<DOC;
But if you have a lot of text to print,
you can use a "here document" and create
a literal string that runs until the
delimiter that was declared with <<.
DOC
print "..and now we're back to regular code.\n";
You can read more about here-documents in the manual: see perldoc perlop.
You’ve all missed the point!
It’s defining a user-defined property for use in \p{MyDigit} and \P{MyDigit} using regular expressions.
It’s like these:
sub InKana {
return <<'END';
3040 309F
30A0 30FF
END
}
Alternatively, you could define it in terms of existing property names:
sub InKana {
return <<'END';
+utf8::InHiragana
+utf8::InKatakana
END
}
You can also do set subtraction using a "C<->" prefix. Suppose you only
wanted the actual characters, not just the block ranges of characters.
You could weed out all the undefined ones like this:
sub IsKana {
return <<'END';
+utf8::InHiragana
+utf8::InKatakana
-utf8::IsCn
END
}
You can also start with a complemented character set using the "C" prefix:
sub IsNotKana {
return <<'END';
!utf8::InHiragana
-utf8::InKatakana
+utf8::IsCn
END
}
I figure I must be right, since I’m speaking ex camelis. :)
It uses something called a Here Document to return a string "0030\t0039"
It returns the string "0030\t0039\n" (\t being a tab and \n a newline that is being added because the line ends in a newline (obviously)).
<<FOO
sometext
FOO
Is a so-called heredoc, a way to conveniently write multi-line strings (though here it is used with only one line).
You can help yourself by trying a simple experiment:
C:\Temp> cat t.pl
#!/usr/bin/perl
use strict; use warnings;
print MyDigit();
sub MyDigit {
return <<END;
0030\t0039
END
}
Output:
C:\Temp> t | xxd
0000000: 2020 2020 3030 3330 0930 3033 390d 0a 0030.0039..
Now, in your case, the END is not lined up at the beginning of the line, so you should have gotten the message:
Can't find string terminator "END" anywhere before EOF at …