What do you think how the code could be rewritten to avoid these warnings?
Use
no warnings 'uninitialized';
in the inner scope where you do the arithmetics.
That tells the reader of your code knows exactly that the undef being interpreted as 0 is intentional. The effect of the "no warnings" is limited to the scope it is in, so there's no harm for the program as a whole.
Perl 6 - links to (nearly) everything that is Perl 6.
| [reply] [d/l] |
For less typing, tighter "scope" and to avoid suppressing other warnings at the same time, you can simply say $1||0, or in this case ($+{'min'}||0).
| [reply] [d/l] [select] |
I knew somebody would suggest this method :-)
It's just that when I add code to surpress a warning, "switch off the warning" feels more direct to me than "avoid the warnings".
Since it's not an error, but a warning, using undef as 0 is legitime, and I find avoiding the feature at all rather backwards. After all warnings should help me write better code, not force me to write different application logic.
Perl 6 - links to (nearly) everything that is Perl 6.
| [reply] |
Moritz has the answer to your question.
I have some concerns about your approach.
The formats you list,
min:sec,frac e.g. "5:29,11"
sec,frac e.g. "29,11"
sec e.g. "29"
,frac e.g. ",11"
min:sec e.g. "5:29"
someth. else e.g. "abc"
are not normalized. In consequence, you provide no coverage of possibilities such as 5:29,3, which appears to be at least theoretically possible in your schema. Were you to begin by normalizing the data to mm:ss,frac, writing a reliable conversion to seconds would be both more reliable and easier (and which is left as an exercise for the user).
That said, and accepting the un-normalized data, suhailck's solution has much to commend it, but also has a couple glitches:
- Its initial test of the string won't correctly handle mixed alphanumerics, such as 1abc
- Its use of $3/10**2 in calculation of the decimal portion fails to consider the possiobility (again, not in your schema, but, ISTM, distinctly possible) of a single-digit, decimal fraction, such as .3 or a decimal fraction where length > 2, as, for example, .303
So, in part TIMTOWTDI, and in part, perhaps by way of improvement:
#!/usr/bin/perl
use strict;
use warnings;
# 855222
# 1) min:sec,frac e.g. "5:29,11"
# 2) sec,frac e.g. "29,11"
# 3) sec e.g. "29"
# 4) ,frac e.g. ",11"
# 5) min:sec e.g. "5:29"
# 6) someth. else e.g. "abc"
my $seconds;
my @times=("5:29,11", "29,11", "29:00", "29:00,3", "29:00,303","29:13,
+07", ",11", "0:11", "5:29", "abc", "1abc");
for my $time(@times) {
no warnings 'uninitialized';
if ($time =~ /[a-z]/i) { # won't catch non-"time-ish" symbols s
+uch as ?, #, @, etc
# $seconds = -1; # my personal preference is for an explicit er
+ror statement
print "Data not recognized: $time\n";
next;
} else {
($seconds = $time) =~ s<^(?:(\d+):)?(\d+)?(?:,(\d+))?$><$1*60+$2+$
+3/(10**length($3))>e;
}
print "$time = $seconds \n";
}
which produces:
5:29,11 = 329.11
29,11 = 29.11
29:00 = 1740
29:00,3 = 1740.3
29:00,303 = 1740.303
29:13,07 = 1753.07
,11 = 0.11
0:11 = 11
5:29 = 329
Data not recognized: abc
Data not recognized: 1abc
| [reply] [d/l] [select] |
Thank you all for your answers.
Now I wrote the following code. It is a bit longer, but more readable I think. And so I'm sure that it really is doing what I want. And I also wrote a function which is doing the way back.
use strict;
use warnings;
my @time_str_list = ( "abc", "1abc", ":", ":,", "5:29,11", "29,11", "2
+9:00", "29:00,3", "29:00,303", "9:13,07", ",11", "0:11", "5:29" );
my $seconds;
for my $time_str ( @time_str_list )
{
&convertTimeStrToSeconds($time_str, \$seconds);
next unless defined $seconds;
print("$time_str\n");
print("$seconds\n");
&convertSecondsToTimeStr($seconds, \$time_str);
next unless defined $time_str;
print("$time_str\n");
print("\n");
}
sub convertTimeStrToSeconds
{
my ($time_str, $ref_seconds) = @_;
# 1) min:sec,frac e.g. "5:29,11"
# 2) sec,frac e.g. "29,11"
# 3) sec e.g. "29"
# 4) ,frac e.g. ",11"
# 5) min:sec e.g. "5:29"
# 6) someth. else e.g. "abc"
$$ref_seconds = undef; # 6)
# remove leading and trailing whitespace characters
$time_str =~ s/^\s+|\s+$//g;
# valid time string
if( ($time_str =~ m/^(?<min>\d+):(?<sec>\d+),(?<frac>\d+)$/) ||# 1
+)
($time_str =~ m/^(?<sec>\d+),(?<frac>\d+)$/) || # 2)
($time_str =~ m/^(?<sec>\d+)$/) || # 3)
($time_str =~ m/^,(?<frac>\d+)$/) || # 4)
($time_str =~ m/^(?<min>\d+):(?<sec>\d+)$/) ) # 5)
{
no warnings 'uninitialized'; # intention: undef treated as zer
+o
$$ref_seconds = ($+{'min'} * 60.0) + $+{'sec'} + ($+{'frac'}/(
+10**length($+{'frac'})));
}
}
sub convertSecondsToTimeStr
{
my ($seconds, $ref_time_str) = @_;
$$ref_time_str = undef;
if( $seconds > 0 )
{
my $min = int($seconds / 60.0);
my $sec = $seconds % 60.0;
my $frac = int(sprintf("%.2f", ($seconds - int($seconds))*100.
+0));
$$ref_time_str = "$min:" if( $min > 0 );
$$ref_time_str .= sprintf("%02d", $sec) if ( ($min > 0) || ($s
+ec > 0) );
$$ref_time_str .= ",$frac" if ( $frac > 0 );
}
}
With the error handling you are right. To set $seconds to -1 is not that good. Error handling is in general for me a difficult thing. I'm not sure what is the best way. Now I'm setting the output parameter to undef. Is this good style? Of course I could print a error message. Or I could return undef. Let's assume this function would be part of an official module. What would be the best error handling?
Thank you and Greetings
Dirk | [reply] [d/l] [select] |
| [reply] |
perl -le '@times=qw(5:29,11 29,11 29 ,11 5:29 abc);
foreach my $time (@times){
if($time!~/[0-9:,]+/) {$seconds=-1}
else { ($seconds=$time)=~s{^(?:(\d+):)?(\d+)?(?:,(\d+))?$}{$1*60+$2+$3
+/10**2}e;}
print $seconds}'
329.11
29.11
29
0.11
329
-1
~suhail | [reply] [d/l] |