#!/usr/bin/perl
use DateTime;
$a = DateTime->new(year=>1952,month=>10,day=>21);
$b = DateTime->new(year=>2015,month=>10,day=>31);
$dif = $b-$a;
print $dif->years() ." ". $dif->months() ." ". $dif->days();
# result: 63 0 3
Where does it get the 3 days from? My expectation is 63 0 10.
#!/usr/bin/perl
use DateTime;
$a = DateTime->new(year=>1952,month=>11,day=>1);
$b = DateTime->new(year=>2015,month=>10,day=>31);
$dif = $b-$a;
print $dif->years() ." ". $dif->months() ." ". $dif->days();
# result 62 11 2
My expectation for this one is 62 11 31 or so.
I am trying to do some basic date of birth to age math. The month and year seem to work as I expect but the day seems unpredictable. I have read the CPAN documentation but I still do not understand.
$dif->years, $diff->months and in particular $diff->days do not do what you expect. From the DateTime::Duration documentation...
These methods return numbers indicating how many of the given unit the
object represents, after having done a conversion to any larger units. For
example, days are first converted to weeks, and then the remainder is
returned. These numbers are always positive.
Here's what each method returns:
$dur->years() == abs( $dur->in_units('years') )
$dur->months() == abs( ( $dur->in_units( 'months', 'years' ) )[0] )
$dur->weeks() == abs( $dur->in_units( 'weeks' ) )
$dur->days() == abs( ( $dur->in_units( 'days', 'weeks' ) )[0] )
If you find this confusing, so do I.
What you want is in_units.
# 63 0 10
say join " ", $dif->in_units('years', 'months', 'days');
Related
I have to write a Perl script that converts a binary number, specified as an
argument, to a decimal number. In the question there's a hint to use the reverse function.
We have to assume that the binary number is in this format
EDIT: This is what I've progressed to (note this is code from my textbook that I've messed with):
#!/usr/bin/perl
# dec2.pl: Converts decimal number to binary
#
die("No arguments\n") if ( $#ARGV == -1 ) ;
foreach $number (#ARGV) {
$original_number = $number ;
until ($number == 0 ) {
$bit = $number % 2 ;
unshift (#bit_arr, $bit) ;
$number = int($number / 2 );
}
$binary_number = join ("", #bit_arr) ;
print reverse ("The decimal number of $binary_number is $original_number\n");
$#bit_arr = -1;
}
When executed:
>./binary.pl 8
The decimal number of 1000 is 8
I don't know how to word it to make the program know to add up all of the 1's in the number that is inputted.
You could just use sprintf to do the converting for you...
sprintf("%d", 0b010101); # Binary string 010101 -> Decimal 21
sprintf("%b", 21) # Decimal 21 -> Binary 010101 string
Of course, you can also just eval a binary string with 0b in front to indicate binary:
my $binary_string = '010101';
my $decimal = eval("0b$binary"); # 21
You don't have to use reverse, but it makes it easy to think about the problem with respect to exponents and array indices.
use strict;
use warnings;
my $str = '111110100';
my #bits = reverse(split(//, $str));
my $sum = 0;
for my $i (0 .. $#bits) {
next unless $bits[$i];
$sum += 2 ** $i;
}
First of all, you are suppose to convert from a binary to decimal, not the other way around, which you means you take an input like $binary = '1011001';.
The first thing you need to do is obtain the individual bits (a0, a1, etc) from that. We're talking about splitting the string into its individual digits.
for my $bit (split(//, $binary)) {
...
}
That should be a great starting point. With that, you have all that you need to apply the following refactoring of the formula you posted:
n = ( ( ( ... )*2 + a2 )*2 + a1 )*2 + a0
[I have no idea why reverse would be recommended. It's possible to use it, but it's suboptimal.]
I have an array
#array = ( 'Apr 11 21:14:25',
'Apr 11 21:10:10',
'Apr 11 21:09:10',
'Apr 11 21:07:10',
);
Here I want to comapare time stamps in a array
Process:
First value in a array should compare with second value to check time difference of 2 minutes or not?
second value in a array should compare with third value and again should check time difference of 2 minutes.
Same process should go on
Any Ideas on How to acheive this in perl?
We don't really just hand out answers without you making an effort first. But it's lunchtime and I wanted a simple programming problem to work on.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
my #times = (
'Apr 11 21:14:25',
'Apr 11 21:10:10',
'Apr 11 21:09:10',
'Apr 11 21:07:10',
);
# Need a year for this to make sense. Let's use
# the current one.
my $year = localtime->year;
# Convert the string dates to Time::Piece objects
my #timepieces = map {
Time::Piece->strptime("$year $_", '%Y %b %d %H:%M:%S')
} #times;
for (1 .. $#times) {
say "Comparing $times[$_ - 1] with $times[$_]";
# Use abs() so we don't care which is larger.
my $seconds = abs($timepieces[$_ - 1] - $timepieces[$_]);
if ($seconds == 120) {
say 'Exactly two minutes difference';
} elsif ($seconds > 120) {
say 'More than two minutes difference';
} else {
say 'Less than two minutes difference';
}
}
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;
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.
I have created the following subroutine gender to randomly print string MALE or FEMALE. When subroutine is invoked, the print command suffixes a "1" at the end of the string. See the sample code and output below:
sub gender {
if ( (int rand(100)) >50) {
print "MALE ";
}
else {
print "FEMALE";
}
}
foreach (1..5) {
print &gender, "\n";
}
Notice a "1" is suffixed to "MALE" OR "FEMALE"
OUTPUT:
FEMALE1
FEMALE1
MALE 1
MALE 1
FEMALE1
MALE 1
I am using perl v5.8.9 v5.8.9 built for MSWin32-x86-multi-thread
Binary build 826 [290470] provided by ActiveState http://www.ActiveState.com
Built May 24 2009 09:21:05
print &gender
calls the gender function and prints what it returns. gender itself, as the last thing it does in either branch, prints a string. Implicitly, it returns the result of the last expression in it (the print "MALE" or print "FEMALE"), and print, when it succeeds, returns 1.
So either do this:
sub gender { if ( rand(100) >= 50 ) {print "MALE ";} else {print "FEMALE";}}
foreach (1..5) { &gender(); print "\n"; }
or this:
sub gender { if ( rand(100) >= 50 ) {return "MALE ";} else {return "FEMALE";}}
foreach (1..5) { print &gender(), "\n"; }
Also, note that &gender, with & but without parentheses, is a special form of function invocation that isn't usually what people mean to use; either drop the & or add empty parentheses to your call.
I've also corrected the if test to return male 50% of the time and female 50% of the time, instead of 49% and 51% respectively.
Let's get idiomatic with your code:
print gender(), "\n"
for 1..5;
sub gender {
return int rand(100) > 50 ? 'MALE' : 'FEMALE';
}
So, what did I do?
First:
The gender sub should not be called with the & and no parens. This invokes the subroutine on the arguments passed to its caller. This is handy when you have a bunch of common argument sanitizing code. But it is not desirable or needed here.
I put the sub after the other code because I like to read my code from high level to specific--the opposite of how C forces you to organize things. I don't like reading my code from the bottom up, so I did it this way. This is purely a personal preference. Do whatever makes you happy. Or if you have to work with others, follow the standard you've agreed upon.
I shortened foreach to for. They do exactly the same thing, one takes fewer characters.
I used for as a statement modifier. In other words I took a simple statement print $_, "\n"; and tacked the for onto the end. For simple tasks it is nicer than using a block. Again, this is my opinion. Some people decry statement modifiers as evil and unwelcome. If you decide to use them, keep it simple. YMMV.
I got rid of the extra unneeded print ysth mentioned.
Instead of using a big if/else block, I used the ternary operator (OK, it's really just a ternary operator, but people call it the ternary operator). It computes a test value and depending on the boolean value of the test, returns the result one of two expressions. It is handy when you want if/else logic in an assignment.
Without an explicit return, the Perl sub will return the last evaluated value. gender returns a 1 because in both execution paths, it calls print which returns a 1.
You should either be having gender return a string, which the caller then prints, or have gender do the printing, and have the caller not do anything with the return value.
Thank You everyone for helping me out with this. I found a way to make a chart I wanted.
Here is how I finally did it;
print "GENDER NAME AGE HEIGHT WEIGHT \n";
foreach (1..10) { ## Starting foreach loop
$age = int(rand( 50))+10;
$height = int (rand(40)) + 50;
$weight = int (rand (100)) + 100;
sub randchar4bit {(chr int rand(25)+65).(chr int rand(25)+65). (chr int rand(25)+65).(chr int rand(25)+65)};
sub gender { return (int rand(100)>50)? "MALE " : "FEMALE ";} ;
print gender(), " ", &randchar4bit, " $age $height $weight style 1\n";
}; ## closing foreach loop
It generates a nice output:
GENDER NAME AGE HEIGHT WEIGHT
FEMALE HHRN 41 67 165 style 1
MALE HNMF 27 63 187 style 1
MALE NLDB 26 54 165 style 1
FEMALE REMB 33 71 118 style 1
MALE TWEW 10 57 122 style 1
MALE OCSC 35 80 168 style 1
FEMALE TKTR 25 64 179 style 1
MALE GMYN 47 73 123 style 1
MALE YKUG 50 79 148 style 1
FEMALE HDFW 47 73 159 style 1