Re: help with algorithm
by Zaxo (Archbishop) on Nov 17, 2002 at 00:37 UTC
|
Think of this in terms of bits, 2 ** $x == 1 << $x. That allows you to decode with bitwise operations. Here's a simple method that uses a hash to translate between names and numbers.
our %daycode = (
Sunday => 1,
Monday => 2,
Tuesday => 4,
Wednesday => 8,
Thursday => 16,
Friday => 32,
Saturday => 64,
);
our %codeday;
@codeday{ values(%daycode)} = keys %daycode;
sub days {
my $mask = shift || 127;
sort { $daycode($a} <=> $daycode{$b} }
grep { $mask & $daycode{$_} }
keys %daycode;
}
by default, days() returns all the days of the week if no argument is given.
After Compline, Zaxo
| [reply] [d/l] |
|
|
OK, this seems to almost work; I want to make sure I'm using this correctly; I'm trying:
#!/usr/bin/perl
$test = 12;
print "Days mask is $test.\n";
our %daycode = (
Sunday => 1,
Monday => 2,
Tuesday => 4,
Wednesday => 8,
Thursday => 16,
Friday => 32,
Saturday => 64,
);
our %codeday;
@codeday{ values(%daycode)} = keys %daycode;
sub days {
my $mask = shift || 127;
sort { $daycode{$a} <=> $daycode{$b} }
grep { $mask & $daycode{$_} }
keys %daycode;
}
@newtest = days($test);
foreach $num (@newtest) {
print "$newtest[$num]\n";
}
If I try 1 value for $test, like 4, it accurately returns Tuesday. However, if I use a complex value, like say 12, it returns Tuesday, Tuesday (where it should be Tuesday, Wednesday).
Am I doing something wrong in interpreting this? | [reply] [d/l] |
|
|
That code is working fine. You just make an error in the way you check the returned values.
@newtest = days($test);
foreach $num (@newtest) {
print "$newtest[$num]\n";
}
The array, @newtest, is assigned ('Tuesday', 'Wednesday') as expected but you iterate over it and use each element as an index back into @newtest. You are printing $newtest['Tuesday'] and $newtest['Wednesday'] but both 'Tuesday' and 'Wednesday' evaluate to 0 in numerical context. So, You print $newtest[0] twice and $newtest[0] holds 'Tuesday'. That's why you print 'Tuesday' twice.
Try writing that loop as:
foreach $day (@newtest) {
print $day,"\n";
}
-sauoq
"My two cents aren't worth a dime.";
| [reply] [d/l] [select] |
|
|
for my $day (@newtest) {
print $day, $/;
}
You are indexing with strings which numify to zero, so you get $newtest["Wednesday"] as $newtest[0].
After Compline, Zaxo | [reply] [d/l] [select] |
Re: help with algorithm
by BrowserUk (Patriarch) on Nov 17, 2002 at 01:37 UTC
|
#! perl -slw
use strict;
sub getDays{
qw(
Sunday
Monday
Teusday
Wednesday
Thurday
Friday
Saturday
) [ grep{ $_[0] & (1 << $_) } 0..6 ]
}
print "@{[getDays $_]}" for 1 .. 127;
__END__
C:\test>213473
Monday
Teusday
Monday Teusday
Wednesday
Monday Wednesday
Teusday Wednesday
Monday Teusday Wednesday
...118 omitted...
Teusday Wednesday Thurday Friday Saturday Sunday
Monday Teusday Wednesday Thurday Friday Saturday Sunday
C:\test>
| [reply] [d/l] |
Re: help with algorithm
by larsen (Parson) on Nov 17, 2002 at 00:23 UTC
|
First you convert $days_mask[$i] from decimal to binary: look here for an example (and it could be a good opportunity to dig into the mysteries of pack). Then you use the string of binary digits you obtained to inspest the "weeklyness" of an event.
In your example, 10 is 1010 in binary: the 1 in the second position (position number 1, since you count from 0) tells that the event repeats every Monday. The same "trick" for the 1 in the third position. | [reply] [d/l] [select] |
|
|
Ok, maybe I'm a little dense: if I understand you correctly, I should be able to determine "monday" from the 1 in the first position of the binary number "1010". However, how does this work for other values, like say, Tuesday, which would show up as 4, which is 100 in binary??
| [reply] |
|
|
my $Sun = 2**0; # That's 1 or 00000001 in binary.
my $Mon = 2**1; # That's 2 or 00000010 in binary.
my $Tue = 2**2; # That's 4 or 00000100 in binary.
my $Wed = 2**3; # That's 8 or 00001000 in binary.
my $Thu = 2**4; # That's 16 or 00010000 in binary.
my $Fri = 2**5; # That's 32 or 00100000 in binary.
my $Sat = 2**6; # That's 64 or 01000000 in binary.
my $mask = 19; # For example. 00010011 in binary.
print "Sunday\n" if ($mask & $Sun); # True
print "Monday\n" if ($mask & $Mon); # True
print "Tuesday\n" if ($mask & $Tue);
print "Wednesay\n" if ($mask & $Wed);
print "Thursday\n" if ($mask & $Thu); # True
print "Friday\n" if ($mask & $Fri);
print "Saturday\n" if ($mask & $Sat);
-sauoq
"My two cents aren't worth a dime.";
| [reply] [d/l] |
|
|
The first example is 1010, that is (I'm considering the binary digits from right to left):
0 * 20 + 1 * 21 + 0 * 22 + 1 * 23
Your second example is 100. As usual, from right to left...
0 * 20 + 0 * 21 + 1 * 22
Got the pattern? Now I'm going to check what powers of 2 are on (i.e. multiplied by 1). In the first example they are
21 (Monday) and 23 (Wednesday). In the second example only 22, Tuesday.
I hope this will clarify my node.
| [reply] |
Re: help with algorithm
by sauoq (Abbot) on Nov 17, 2002 at 01:14 UTC
|
Your variable name, $days_mask, gives a clue. You are working with a bitmask. As Zaxo pointed out, you need to use a bitwise-and to extract the value of individual bits. If you just want the day numbers you might do it like this:
my @day_numbers = grep $days_mask[$i] & 2**$_, 0..6;
-sauoq
"My two cents aren't worth a dime.";
| [reply] [d/l] |
Re: help with algorithm
by pg (Canon) on Nov 17, 2002 at 01:20 UTC
|
try this, it tested, it works:
for (1 .. 7) {push @days, $_ if ((2 ** $_) & $day_mask)}
| [reply] [d/l] |
|
|
-sauoq
"My two cents aren't worth a dime.";
| [reply] [d/l] |
|
|
Oops, I check back, he said Sunday = 0, you are right.
| [reply] |