From estrabd at yahoo.com Thu Aug 5 10:57:15 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Thu Aug 5 10:57:19 2004 Subject: [Neworleans-pm] Fwd: Perl Quiz-of-the-Week #21 Message-ID: <1091721435.11810.201768952@webmail.messagingengine.com> ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com ----- Original message ----- From: "Mark Jason Dominus" To: perl-qotw@plover.com Date: Thu, 05 Aug 2004 11:44:43 -0400 Subject: Perl Quiz-of-the-Week #21 IMPORTANT: Please do not post solutions, hints, or other spoilers until at least 60 hours after the date of this message. Thanks. IMPORTANTE: Por favor, no envi?is soluciones, pistas, o cualquier otra cosa que pueda echar a perder la resoluci?n del problema hasta que hayan pasado por lo menos 60 horas desde el env?o de este mensaje. Gracias. WICHTIG: Bitte schicken Sie keine L?sungen, Tipps oder Hinweise f?r diese Aufgabe vor Ablauf von 60 Stunden nach dem Datum dieser Mail. Danke. VNIMANIE: Pozhalujsta ne shlite reshenija, nameki na reshenija, i voobshe lyubye podskazki v techenie po krajnej mere 60 chasov ot daty etogo soobshenija. Spasibo. Qing3 Zhu4Yi4: Qing3 Ning2 Deng3Dao4 Jie1Dao4 Ben3 Xin4Xi2 Zhi1Hou4 60 Xiao3Shi2, Zai4 Fa1Biao3 Jie3Da2, Ti2Shi4, Huo4 Qi2Ta1 Hui4 Xie4Lou4 Da2An4 De5 Jian4Yi4. Xie4Xie4. ---------------------------------------------------------------- You will write a program to perform scheduling. As we all know, tasks sometimes take longer than expected. Sometimes when this happens, the final deadline of the project is affected; sometimes it isn't. For example, consider the four tasks A, B, C, and D. B and C depend on A, which means that they cannot be started until A is finished. D depends on B and C, and cannot be started until both B and C are finished: .-> B . A :-> D `-> C ' Suppose we expect the four tasks to take the following times: A: 1 day B: 2 days C: 3 days D: 1 day Then we don't expect the project to be finished for at least 5 days: one day to complete A and start C; 3 days to complete C and start D, and another day to finish D. Any delay in any of the three tasks A, C, or D will cause the completion date to slip. We say that A, C, and D are on the "critical path". But B is not on the critical path, because B can go on while C is going on, and can take up to one day longer than expected without delaying the start of D. You will write a program which will calculate critical paths. The input to the program will be a file in the following format: A 1 B 2 A C 3 A D 1 B C FINAL 0 D Each line represents one task. The first field in each line is the name of the task. The second field is the expected duration. If there are any other fields, they are the names of other tasks which must be finished before this task can start. The program will find all the tasks on the critical path to the task named FINAL and will print them out, one per line. It may happen that the input specifies tasks that cannot possibly be completed. For example: A 1 B B 1 A FINAL 0 A B Here A can't start until B is finished, but B can't start until A is finished. In such cases, the program should diagnose an error. [ ADMINISTRATIVE NOTE: Last week's expert quiz turned out to be more subtle than it first appeared. The report and sample solution will be along when it is ready. Thanks for your patience. -MJD ] From joey at joeykelly.net Fri Aug 6 10:28:06 2004 From: joey at joeykelly.net (Joey Kelly) Date: Fri Aug 6 10:15:08 2004 Subject: [Neworleans-pm] IMPORTANT: State radio and TV board wants to license computer techs Message-ID: <200408061028.06221.joey@joeykelly.net> Y'all, sorry for the cross-posting, but this is rather important. The Louisiana Radio and Television Technicians Board is sending out letters to computer techs demanding a yearly licensing fee, on the premise that since a computer can do home entertainment, it must be either a TV or a radio and should be regulated as such. Times Picayune article (bogus login required): http://www.nola.com/business/t-p/index.ssf?/base/money-0/1091633106206812.xml The article quotes a tech saying that if the state were to go with a real computer cunsultant license, he would be more willing to abide by that. I think licensing techs of any kind is a charade, as we all know how useless A + and MCSE testing is to determine whether a tech knows anything at all. Fools pay chump schools who in turn teach to the applicable tests, and the then-certified techs don't even know which end of a mouse to hold, etc. The state's noble goal (that of protecting consumers) has degenerated into a revenue stream only. Slashdot story: http://yro.slashdot.org/article.pl?sid=04/08/05/2235213&tid=103&tid=126&tid=17 Text of an email alert put out by the Louisiana Technology Council: -----Original Message----- From: Mark Lewis [mailto:mlewis@ltc-la.org] Sent: Tuesday, August 03, 2004 11:47 AM To: LTC Information Subject: CRITICAL: State Board Wants to Control Computer Consultants & Technicians To: Business Community and technologists It has come to our attention that the Louisiana State Board of Radio and Television Repair Technicians desires to MANDATE the licensing of Computer Consultants and Technicians by extrapolating Act 428 which was passed by the Louisiana State Legislature in 1958. In a copy of a letter sent to me by @Site Computer Services, the Board states that "home computers provide for television reception and recording and all provide audio/visual playback and recording capabilities. Therefore, in accordance with the existing law (Act 428), the Board has elected to license computer technicians." As the letter from the Board states, the general provisions of Act 428 is to protect the public welfare, aid the department of revenue in collecting sales taxes on labor and on retail prices of material used in the service and maintenance of radio and television receivers, CB radios, playback and recording devices, VCR's, and satellite signal receiving equipment ...... The bottom line is that if any employee is engaged in the repair, maintenance, consultation or training of computer equipment, including hardware, peripherals, and networks, as used in the home (or possibly even the office), they must fill out an application and PAY an annual $55.00 licensing fee (per person). There are other license requirements as well. It appears that this Board is sending letters to companies and individuals that perform most all computer services and giving them 15 days to comply with the Board's licensing procedures or face fines and prison penalties. Personally I think this a very broad interpretation of the law and would hurt the technology community if this were to be enforced. In fact, there is also a provision in the Act that specifically states, "the term playback and recording devices does not mean or include playback and recording devices normally designed for use as office equipment...." This seems to make a lot more sense when computers are involved. I am sending you this email for the very purpose of notifying you of this initiative by the State Board. If you choose to voice your opinion, please do so immediately by contacting your congressional delegate or by writing or calling Mr Stanley Brohn, Secretary of the State Board who wrote the letter: State of Louisiana Radio and Television Technicians Board 6554 Florida Ave. - Suite 109 Baton Rouge, LA 70806 (225) 231-4710 You can access the letter sent by Mr. Brohn to @Site Computer along with their response at our web site: http://events.ltc-la.org/downloads/State_Board_Letter.pdf (650k) Please pass this information on to anyone you feel might be interested in this initiative and if you feel they would like to voice their opinion. Again, feel free to contact your state legislators and let them know your feelings. If you have any questions, please feel free to call me as well. Special thanks goes to Jarrod Broussard of @Site Computer Services for drawing this to our attention. Mark S. Lewis, President Louisiana Technology Council Louisiana - Tech Capital of the South "Quality Always Wins" Voice: (504) 304-2911 URL: http://www.LTC-LA.org -- Joey Kelly < Minister of the Gospel | Linux Consultant > http://joeykelly.net "I may have invented it, but Bill made it famous." --- David Bradley, the IBM employee that invented CTRL-ALT-DEL From estrabd at yahoo.com Mon Aug 9 17:13:49 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Mon Aug 9 17:13:53 2004 Subject: [Neworleans-pm] Fwd: Solutions and Discussion for Perl Quiz of the Week #20 (Expert Edition) Message-ID: <1092089629.32123.202008254@webmail.messagingengine.com> ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com ----- Original message ----- From: "Bradley K. Embree" To: perl-qotw@plover.com Date: Mon, 09 Aug 2004 13:05:37 -0400 Subject: Solutions and Discussion for Perl Quiz of the Week #20 (Expert Edition) Sample solutions and discussion Perl Expert Quiz of The Week #20 (20040729) This week's quiz is to write a limited replacement for the CPAN 'MLDBM' module. The purpose of MLDBM is to allow storage of complex Perl data structures in a DBM file. The typical DBM implementation (such as SDBM_File, which comes standard with Perl) allows the programmer to manipulate a disk database as if it were an ordinary hash variable. One says something like tie %hash, 'SDBM_File', $filename; and thereafter, $hash{"key"} = "value"; stores "value" in the disk file under the key "key", and $x = $hash{"key"}; retrieves the value again. But most DBM implementations are limited to storing plain strings as values. Attempting to store undef as a value actually stores the empty string instead, so that $hash{"key"} = undef; print defined($hash{"key"}) ? "oops" : "OK"; prints "oops". Similarly, attempting to store a compound object such as [1, 2, 3] or {name => "bill", numbers => [1, 2, 3] } actually stores a useless string like "ARRAY(0x436c1d)" or "HASH(0x436c1d)". Similarly, $hash{"this"}{"that"} = "ouch"; causes a "strict refs" failure, if you have "strict refs" checking on, and a bizarre error if not. (Sub-quiz: Why?) The purpose of MLDBM is to work around this. (See http://search.cpan.org/~chamas/MLDBM-2.01/lib/MLDBM.pm for complete details.) Your task this week is to write a version of MLDBM. It should be a tied hash class named 'ML_DBM', such that after tie %mldbm, 'ML_DBM', \%hash; the user can access %mldbm as if it were an ordinary hash, but all data apparently stored in %mldbm is actually stored in %hash instead. For example, this must work: $mldbm{'array'} = [5,6,7]; print $mldbm{'array'}[2]; # This must print 7 as must this: $mldbm{'hash'} = { I => { like => "pie" } } print $mldbm{'hash'}{'I'}{'like'}; # This must print "pie" and this: $mldbm{'a'}{'a2'} = 'AA'; $z = $mldbm{'a'}; $z->{'b2'} = "BB"; print $mldbm{'a'}{'b2'}; # This must print "BB" There is a trivial solution to this, which is just to store the values into %hash and fetch them back as requested. However, this quiz has a restriction which rules out that solution: The values that ML_DBM stores in %hash must always be ordinary strings. The idea is that the user can tie %hash to their favorite DBM implementation, and then use that as the backing hash for ML_DBM: tie %hash => 'NDBM_File', $filename or die ...; tie %db => 'ML_DBM', \%hash; and ML_DBM will (unknowingly, but faithfully) store the data into the underlying disk file via %hash. ML_DBM must restrict the values it stores into %hash to ordinary strings, or else the underlying DBM implementation (NDBM_File in this case) will not faithfully preserve them. ---------------------------------------------------------------- First off, apologies for the late summary. [ Most of the lateness in the report is my fault, not Bradley's; he sent me his postmortem several days ago, and I held onto it for a while because I was playing around with the problem some more. - MJD ] Executive Summary: See http://search.cpan.org/~pinyan/MLDBM-Easy-0.01/lib/MLDBM/Easy.pm My Aborted Attempt: Since I had never played with tie'ing I thought I would make a go of this quiz (although MJD warned me it would be a difficult one). [ It turned out to be more difficult than I realized. - MJD ] My first stop was: perldoc perltie which I perused for several minutes gathering what information I thought necessary for my attempt. Armed with my newfound knowledge I chose to focus on something mentioned in the quiz: 'There is a trivial solution to this, which is just to store the values into %hash and fetch them back as requested.' I decided that this would be my first goal, simply to implement the trivial solution. I quickly coded up a (far from complete) MyHash.pm that did just that: package MyHash; use strict; use warnings; sub TIEHASH { my ( $class, $hash ) = @_; return bless $hash, $class; } sub FETCH { my ( $self, $key ) = @_; return $self->{$key}; } sub STORE { my ( $self, $key, $value ) = @_; $self->{$key} = $value; } package main; my ( %hash, %tied ); tie %tied, 'MyHash', \%hash; $tied{'foo'}->{'bar'}->{'baz'} = 'test'; print $tied{'foo'}->{'bar'}->{'baz'}, "\n"; Ok, that worked. My next step was to make use of Data::Dumper to see what was actually being passed to STORE: sub STORE { my ( $self, $key, $value ) = @_; print Dumper \@_; $self->{$key} = $value; } I assumed it would be a reference to a data structure that would have to be serialized before being stored. Then it would have to be unserialized (deserialized?) in the FETCH method. I had even gone so far as deciding to try and use YAML for the serialization. Imagine my confusion upon seeing this output from 'perl MyHash.pm': $VAR1 = [ bless( {}, 'MyHash' ), 'foo', {} ]; test 'So the value passed to STORE is not yet fleshed out?' I thought to myself. 'But how can you serialize data that is not there?'. The next thought was: 'Now what?' So I decided to actually visit http://search.cpan.org/~chamas/MLDBM-2.01/lib/MLDBM.pm as recommended in the quiz. There I quickly learned of the limitations of MLDBM with regards to working with references. I decided that I could implement a solution that worked but was under the same restrictions as MLDBM. I pseudo-coded until I was satisfied that I could at least achieve that modest end. I then turned my thoughts to ways of overcoming those restrictions. In all honesty there was not much there. I briefly tried to think of a way to turn: $tied{'foo'}->{'bar'}->{'baz'} = 'test'; into: # where | is some arbitrary separator character %hash = ( 'foo' => 'foo|bar', 'foo|bar' => 'foo|bar|baz', 'foo|bar|baz' => 'test' ); The only thing I could think of was to make use of tie'd hashes that would know their 'namespace' and define a STORE method that used the 'namespace' to store the value given. Then my brain exploded and I gave up for the night. That is as far as I got and I should now turn to submissions that actually solved the given problem. Submissions: I counted three different submissions, two from Randy W. Sims and one from Michael C. Toren. One of the submissions from Randy W. Sims has the same restrictions as MLDBM (with the exception of correctly storing an undef value) and so I will only consider his second submission. The similarity between the two (fully conformant) submissions is that both make use of a 'frontend' hash that allows the serialization to be delayed until the data structures being stored are fully fleshed out. After this delay the serialized data is then stored in the 'backend' hash that was passed to tie. Where the two solutions differ is in how the data is serialized as well as how long the serialization is delayed. Michael C. Toren implemented his own serialization technique while Randy W. Sims made use of Data::Dumper. I have not spent enough time looking at Michael C. Toren's serialization code to feel confident in making any comparisons between it and Data::Dumper. Randy W. Sims' submission delays the serialization of data structures until the next operation on the tied hash while Michael C. Toren's submission delays the serialization until the hash is untied or destroyed. From my understanding of their code Randy W. Sims' method has an advantage since the frontend hash will never contain the entire contents of the backend hash unlike Michael C. Toren's submission. Regardless of their differences, both submissions from Randy W. Sims and Michael C. Toren solved the given quiz successfully. Congratulations to them. ---------------------------------------------------------------- Sub-quiz: Similarly, attempting to store a compound object such as [1, 2, 3] or {name => "bill", numbers => [1, 2, 3] } actually stores a useless string like "ARRAY(0x436c1d)" or "HASH(0x436c1d)". Similarly, $hash{"this"}{"that"} = "ouch"; causes a "strict refs" failure, if you have "strict refs" checking on, and a bizarre error if not. (Sub-quiz: Why?) ---------------------------------------------------------------- I did not see anyone comment on the given sub-quiz but here are my observations: The code: use strict; use warnings; use Fcntl; # For O_RDWR, O_CREAT, etc. use SDBM_File; my %h; tie( %h, 'SDBM_File', 'filename', O_RDWR|O_CREAT, 0666 ) or die "Couldn't tie SDBM file 'filename': $!; aborting"; $h{"this"}{"that"} = "ouch"; gives an error: Can't use string ("HASH(0x1abedc4)") as a HASH ref while "strict refs" in use at SDBM_test.pl line 11. which seems to be self-explanatory. Removing the 'use strict' did not produce a bizarre error as was indicated it should so I cannot comment on that part of the sub-quiz (perhaps my version of Perl affects the expected error?). [ The "bizarre error" is not a visible failure; it's an invisible erroneous behavior. Perl has a feature called a "symbolic reference", in which an ordinary string, say "foo", can be used as if it were a reference. The result of using "foo" in a place where Perl expects an array reference is that Perl silently treats the string as though it were a reference to the array @foo. Similarly, if "foo" is used as if it were a hash reference, it behaves as though it were a reference to %foo. In advanced applications, this feature can be very useful, but it is also the cause of many common error that are extremely difficult to track down. Let's consider this simple function, which is something like Perl's built-in "push" operator: sub my_push { my ($aref, @items) = @_; push @$aref, @items; } Normally, 'my_push' should be invoked like this: my_push(\@my_array, 1, 2, 3); print "@my_array"; # prints 1 2 3 which pushes 1, 2, 3 onto the end of @my_array. This works also: my $aref = \@my_array; my_push($aref, 1, 2, 3); print "@my_array"; # prints 1 2 3 But this does not: my $aref = \@my_array; my_push("$aref", 1, 2, 3); print "@my_array"; # prints NOTHING! The quotation marks around $aref turn $aref from an array reference into a string, something like "ARRAY(0x436c1d)". 'my_push' then treats this string as though it were a reference, but it isn't. Perl pushes the 1, 2, 3 not into @my_array but into the array with the extremely bizarre name @"ARRAY(0x436c1d)". The programmer thought they were storing the 1, 2, 3 into a known place, but actually the 1, 2, 3 are now lost, stored into a mystery bizarro array. This is exactly the kind of error that "strict refs" was created to diagnose. In Pr. Embree's example code, this error is exercised: my %h; tie( %h, 'SDBM_File', 'filename', O_RDWR|O_CREAT, 0666 ) or die "Couldn't tie SDBM file 'filename': $!; aborting"; $h{"this"}{"that"} = "ouch"; Perl creates a new, fresh hash, {}, and tries to store a reference to it into the SDBM file under the key "this". But doing this convers the reference to a string, and what is actually stored in the file is the string "HASH(0x1abedc4)". Perl then fetches this value back and uses it as a reference to a hash, in which "ouch" is stored under key "that". In an ordinary hash, this works properly. But with the SDBM file, what is used as a hash reference is the string "HASH(0x1abedc4)". If "strict refs" is on, using this string as if it were a hash reference is a fatal error. With "strict refs" disabled, Perl stores the string "ouch" into ${"HASH(0x1abedc4)"}{"that"}. There is a good chance that this data will be lost for ever. Even if it isn't, it is certainly not what the programmer intended. That is the "bizarre error". My thanks to Pr. Embree and also to everyone who contributed, whether visibly or not. -- MJD. ] From estrabd at yahoo.com Wed Aug 11 12:46:32 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Wed Aug 11 12:46:39 2004 Subject: [Neworleans-pm] Fwd: Solutions and Discussion for Perl Quiz of the Week #21 Message-ID: <1092246392.28094.202148476@webmail.messagingengine.com> ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com ----- Original message ----- From: "Ronald J Kimball" To: perl-qotw@plover.com Date: Wed, 11 Aug 2004 09:42:53 -0400 Subject: Solutions and Discussion for Perl Quiz of the Week #21 Sample solutions and discussion Perl Quiz of The Week #21 (20040805) You will write a program to perform scheduling. As we all know, tasks sometimes take longer than expected. Sometimes when this happens, the final deadline of the project is affected; sometimes it isn't. For example, consider the four tasks A, B, C, and D. B and C depend on A, which means that they cannot be started until A is finished. D depends on B and C, and cannot be started until both B and C are finished: .-> B . A :-> D `-> C ' Suppose we expect the four tasks to take the following times: A: 1 day B: 2 days C: 3 days D: 1 day Then we don't expect the project to be finished for at least 5 days: one day to complete A and start C; 3 days to complete C and start D, and another day to finish D. Any delay in any of the three tasks A, C, or D will cause the completion date to slip. We say that A, C, and D are on the "critical path". But B is not on the critical path, because B can go on while C is going on, and can take up to one day longer than expected without delaying the start of D. You will write a program which will calculate critical paths. The input to the program will be a file in the following format: A 1 B 2 A C 3 A D 1 B C FINAL 0 D Each line represents one task. The first field in each line is the name of the task. The second field is the expected duration. If there are any other fields, they are the names of other tasks which must be finished before this task can start. The program will find all the tasks on the critical path to the task named FINAL and will print them out, one per line. It may happen that the input specifies tasks that cannot possibly be completed. For example: A 1 B B 1 A FINAL 0 A B Here A can't start until B is finished, but B can't start until A is finished. In such cases, the program should diagnose an error. ---------------------------------------------------------------- Ten different people provided solutions for this quiz. Seven of the ten solutions used recursion, a natural fit for this problem. Four of the solutions used a single pass over the schedule, finding the critical path for each node by finding the critical path for each of its dependencies. Another four used two passes, first calculating start and end times for each task, then finding the critical path to FINAL in the second pass. The remaining two solutions built a list of all the paths to FINAL and chose the one with the longest duration. Two of the solutions stuck with Llama-only features, avoiding references in particular. (That's always an option, but not a requirement, for the regular quizzes.) One solution was object-oriented. Also, one of the solutions was written in Ruby. I wrote a test suite to test the solutions with various valid and invalid input. When viewing these results, keep in mind that the problem description did not specify how to handle: tasks being both direct and indirect dependencies of other tasks (valid #5, #6) blank lines in the input (valid #10) invalid input other than cycles (invalid #4, #5, #6) Valid Input Invalid Input 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 .--------------------------------.--------------------. rjk | y y y y y y y y n y | y y y y y y | roel | y y y y n n y y y * | y y y n n n | xavier | y y y y y y y y y * | y y y n n n | christian | y y y y n n y y y n | y y y y y y | mjd2 | y y y y n y y y y y | y y y n - n | rod | y y y y y y y y y y | y y y n + y | adrian | y y y y y y y y y y | y y y n n n | jurgen | y y y y y y y y y * | y y y n n n | jean | y y y y y y y y y y | y y y n y y | zsban | y y y y y y y y y y | y y y y y y | `--------------------------------'--------------------' * Successful, but with warnings - Questionable; no output at all + Questionable; error message slightly misleading Valid Input 1. Simple 1 2. Simple 2 3. Multiple Critical Paths 4. Complicated 5. FINAL depends on everything 6. Extra dependencies 7. Extra tasks 8. No dependency for FINAL 9. Out of order 10. Blank lines Invalid Input 1. 1-cycle 2. 2-cycle 3. 3-cycle 4. Repeated label 5. No FINAL 6. Missing dependency I'll start by discussing my own solution, because it uses only features from the Llama (AFAIK), it's fairly short, and I think it's an interesting approach. I started by deciding that I would require the input to be in order. (That is, a task must be given before any tasks that depend on it.) This allowed me to easily create a list of all the paths through the schedule (and also made it impossible to have cycles). #!/usrs/local/bin/perl -w use strict; my %paths; my %labels; while (<>) { next unless /\S/; my($label, $duration, @dependencies) = split; die "Duplicate label $label on line $..\n" if $labels{$label}++; if (@dependencies) { foreach my $dependency (@dependencies) { die "Label $label depends on itself on line $..\n" if $label eq $dependency; die "Unknown dependency $dependency on line $..\n" if !$labels{$dependency}; foreach my $path (keys %paths) { if ($path =~ / \Q$dependency\E\z/) { $paths{"$path $label"} = $paths{$path} + $duration; } } } } else { $paths{" $label"} = $duration; } } The heart of the program is hash that contains all the paths through the schedule as keys, and the durations of the paths as values. For example, the hash might look like this: (' A' => 1, ' A B' => 3, ' A C' => 4, ' A B D' => 4, ' A C D' => 5, ' A B D FINAL' => 4, ' A C D FINAL' => 5) The leading space makes the regexes simpler. If a task has dependencies, the script finds each path in the hash that ends with one of those dependencies and appends the task to make a new path. If a task has no dependencies, a new path started consisting just of that task. die "No FINAL label.\n" if !$labels{'FINAL'}; my @critical; my $max = -1; foreach my $path (keys %paths) { next if $path !~ / FINAL\z/; if ($paths{$path} > $max) { $max = $paths{$path}; @critical = $path; } elsif ($paths{$path} == $max) { push @critical, $path; } } Now that the script has all possible paths, it simply looks at those that end at final and grabs the one(s) with the longest duration. die "No critical paths.\n" if !@critical; foreach my $path (@critical) { my @path = split ' ', $path; print "$_\n" for @path; print "\n"; } Although this approach is simple and straightforward, it can use a lot of memory! This also highlights another of the redundancies in the problem description; what to do if there are multiple critical paths to FINAL. There are three possible answers: output each path separately (A B D FINAL / A C D FINAL); output any one of the paths (A B D FINAL); output all the paths intermingled (A B C D FINAL). I accepted any of these in my test suite, although personally I feel the first one is best. [ The problem statement asks only for a list of "the tasks on the critical path". The critical path itself isn't the goal; it's the listing of the critical tasks, because these are the tasks whose slippage will delay the end of the project. So option #2 above is simply erroneous. My own preference is for option #3, since #1 is redundant. - MJD ] Now for an example of a recursive solution. I'll discuss the one provided by Rod Adams, which is short and sweet. #!/usr/bin/perl -w my %tasks = (); while (<>) { chomp; next if /^\s*$/; my ($task, $duration, @prereqs) = split /\s+/; @{$tasks{$task}}{'duration', 'prereq', 'painted'} = ($duration, \@prereqs, 0); } First the script builds a data structure containing the duration and prerequisites for each task. The 'painted' value will be used to detect cycles. my ($duration, @path) = CritPath('FINAL'); print "Critical Path: ($duration days)\n", map(" $_\t$tasks{$_}{duration}\n", @path); The call to CritPath('FINAL') will recursively find the desired critical path. sub CritPath { my ($node) = $tasks{$_[0]} || die "Bad prereq $_[0]\n"; $node->{painted}++ and die "Tasks must be acyclic.\n"; If the task is already painted, there's a cycle. my @maxpath = (0); for my $req (@{$node->{prereq}}) { my @path = CritPath($req); @maxpath = @path if $path[0] > $maxpath[0]; } Find the critical path for each of the task's dependencies and grab the one with the longest duration. $node->{painted}--; Unpaint the task because there may be a separate, non-cyclic path to it. $maxpath[0] += $node->{duration}; return (@maxpath, $_[0]); } Add the current task to the end of its critical path and return. (If the task has no dependencies, its critical path is just itself.) There was an interesting bug in Rod's script. The Bad prereq check in the original was: my ($node) = $tasks{$_[0]} or die "Bad prereq $_[0]\n"; Do you see the problem? I didn't at first. It turns out the precedence is wrong. This line is parsed as: (my ($node) = $tasks{$_[0]}) or die "Bad prereq $_[0]\n"; A list assignment in scalar context returns the number of values assigned. In this case that is always 1, even if the value happens to be undef. So the conditional is always true and the die can never execute. In the code above, I fixed this by changing to the high precedence ||-operator, which is parsed as: my ($node) = ($tasks{$_[0]} || die "Bad prereq $_[0]\n"); Another way to fix it is to use scalar assignment instead of list assignment: my $node = $tasks{$_[0]} or die "Bad prereq $_[0]\n"; Some additional notes: 1. Mine was the only solution that required the input to be given in order. Something like Mark-Jason Dominus's tsort() sub could be used to fix this shortcoming. 2. All the solutions passed the straightforward test cases. However, Zsban Ambrus's solution (which happens to be written in Ruby) was the only one that passed all the additional tests as well. That's some nice defensive programming! 3. MJD's first solution went into an infinite loop on the 'Extra tasks' test, so I left it out and just used his second one. 4. I'm familiar with MJD's philosophy on warnings from an earlier quiz. If he had turned them on, however, he would have noticed that he was still printing values from %slack even though he had removed the code that calculated those values. [ I did notice this, even without '-w'. My mistake was to leave the debugging lines uncommented in the version of the program I posted to the mailing list. - MJD ] Thanks to everyone who participated! Additional thanks to Roel van der Steen for his sample input, which revealed a bug in an early version of one of the other solutions, and to Jurgen Pletinckx for pointing to sample input from a similar problem on the perl golf mailing list. [ Ronald also set me an archive file containing the solutions and test programs; I have placed this at http://perl.plover.com/qotw/misc/r021/quiz21.tgz . Thank you, Ronald! I expect to send the new quiz later today. - MJD ] From joey at joeykelly.net Wed Aug 11 10:50:36 2004 From: joey at joeykelly.net (Joey Kelly) Date: Wed Aug 11 23:51:32 2004 Subject: [Neworleans-pm] handy weather site Message-ID: <200408111050.36139.joey@joeykelly.net> Y'all, I keep a weather page that might be of some use to you guys: http://joeykelly.net/weather/neworleans.php The site has a ton of graphics, and may take a couple of minutes to load if you're using a dialup connection with a modem, etc. However, since the whole point is to have helpful, accurate weather maps in one place, the download times shouldn't be an issue. FYI: every time there's an impending hurricane, I post this link to the lists I am subscribed to as a public service. Enjoy. -- Joey Kelly < Minister of the Gospel | Linux Consultant > http://joeykelly.net "I may have invented it, but Bill made it famous." --- David Bradley, the IBM employee that invented CTRL-ALT-DEL From estrabd at yahoo.com Thu Aug 12 07:29:46 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Thu Aug 12 07:29:49 2004 Subject: [Neworleans-pm] Fwd: Perl 'Expert' Quiz-of-the-Week #21 Message-ID: <1092313786.22732.202202501@webmail.messagingengine.com> ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com ----- Original message ----- From: "Mark Jason Dominus" To: perl-qotw@plover.com Date: Thu, 12 Aug 2004 07:44:11 -0400 Subject: Perl 'Expert' Quiz-of-the-Week #21 IMPORTANT: Please do not post solutions, hints, or other spoilers until at least 60 hours after the date of this message. Thanks. IMPORTANTE: Por favor, no envi?is soluciones, pistas, o cualquier otra cosa que pueda echar a perder la resoluci?n del problema hasta que hayan pasado por lo menos 60 horas desde el env?o de este mensaje. Gracias. IMPORTANT: S'il vous pla?t, attendez au minimum 60 heures apr?s la date de ce message avant de poster solutions, indices ou autres r?v?lations. Merci. Qing3 Zhu4Yi4: Qing3 Ning2 Deng3Dao4 Jie1Dao4 Ben3 Xin4Xi2 Zhi1Hou4 60 Xiao3Shi2, Zai4 Fa1Biao3 Jie3Da2, Ti2Shi4, Huo4 Qi2Ta1 Hui4 Xie4Lou4 Da2An4 De5 Jian4Yi4. Xie4Xie4. UWAGA: Prosimy nie publikowac rozwiazan, dodatkowych badz pomocniczych informacjii przez co najmniej 60 godzin od daty tej wiadomosci. Dziekuje. ---------------------------------------------------------------- In the 1960's, the grad students at the University of Chicago math department worked on a series of astoundingly useless and time-consuming puzzles. One of these follows. Consider the set of all possible strings of the alphabet ('a' .. 'z'). Let us agree to consider two strings "equivalent" if the following conditions hold: 1. They contain precisely the same letters, and 2. They both appear in Webster's Third International Dictionary. In such a case, the two strings are considered interchangeable in all contexts. For example, "am" and "ma" are equivalent, and this also implies that "amount" and "maount" are equivalent, as are "grammar" and "grmamar" and "gramamr" and "grmaamr". Moreover, equal letters can be cancelled from the front and back end of any string. For example, "abby" and "baby" are equivalent, and, cancelling the trailing "by", this implies that "ab" and "ba" are also equivalent, and can be exchanged anywhere. When two letters can be exchanged in this way, we say that they "commute". The third floor of the math building at UC had a huge 26x26 chart; the square in column i and row j contained a proof that letters i and j would commute. Sometimes these proofs can be rather elaborate. For example, the dictionary has "dire" and "ride", so, by cancelling the trailing "e"s, one has "dir" = "rid". The dictionary also has dirten = rident (No, I don't know what those mean.) Since "dir" = "rid" we have: rident = dirent and since "rident" = "dirten", dirten = dirent even though "dirent" is not a dictionary word. Cancelling the leading "dir" leaves: ten = ent but ten = net because "ten" and "net" are dictionary words, so ent = net and, cancelling the "t", en = ne and we've just proved that "en" and "ne" commute. This fact might be useful in later proofs. What's the point of all this? Well, the goal is to find out which letters commute with *every* other letter; such letters are said to be in the "center" of the system. As for the *point*, I'm not sure there is one. Apparently the math grads at UC didn't have enough to occupy their time. The chart in the UC math building has since been lost, so your task is to write a program whose input is a word list, with one word per line, and which makes appropriate deductions and eventually computes the center of the system. I don't have the headword list from Webster's Third, but I do have the list from Webster's Second, so let's use that. You can get a copy from http://perl.plover.com/qotw/words/Web2.bz2 http://perl.plover.com/qotw/words/Web2.gz From dave at gnofn.org Fri Aug 13 09:51:00 2004 From: dave at gnofn.org (Dave Cash) Date: Fri Aug 13 09:51:18 2004 Subject: [Neworleans-pm] Perl Mongers Meeting Tonight Message-ID: <20040813094808.K59579@sparkie.gnofn.org> Hello, all. Just a reminder: tonight is our monthly meeting at Fair Grinds Coffee House (3133 Ponce de Leon, just down from the Whole Foods on Esplanade). Normally, we have the upstairs room from 5pm to 7pm, but tonight we only have it from 5pm to 6pm. The meeting can continue downstairs after that if we want it to. Hope to see you all tonight! Dave /L\_/E\_/A\_/R\_/N\_/T\_/E\_/A\_/C\_/H\_/L\_/E\_/A\_/R\_/N\ Dave Cash Power to the People! Frolicking in Fields of Garlic Right On-Line! dave@gnofn.org Dig it all. From EmailLists at SimonDorfman.com Sat Aug 14 04:05:26 2004 From: EmailLists at SimonDorfman.com (Simon Dorfman) Date: Sat Aug 14 04:05:35 2004 Subject: [Neworleans-pm] Project idea and thanks Message-ID: Donnie, David & Joey, 1. Many thanks for your help and ideas about how to get started on my project yesterday. I feel like this is actually something I can accomplish now if I take it in small pieces. 2. Re: the project y'all want to work on together with cvs and such. Here's a project idea for you: http://www.skump.org http://www.skump.org/how_to_play_skump.html Is a simple explanation of the game. http://www.skump.org/web_based_skump.html Is a very basic call for programmer help with some ideas of what the site might be. Check it out, it's a fun game. The thing is, it may not be best suited for perl because it involves capturing drawings. I planned to do it with flash and php back a couple years ago when I put that site online. I don't know if perl could do something like that (maybe in conjunction with imagemagik or something?). Anyway, it's an idea I thought I'd throw out there. Simon From estrabd at yahoo.com Sat Aug 14 12:46:34 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Sat Aug 14 12:46:37 2004 Subject: [Neworleans-pm] summary Message-ID: <1092505594.15262.202339345@webmail.messagingengine.com> If anyone would like to send me a summary of the meeting, I would be happy to post it on the site...thanks! Brett ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com From joey at joeykelly.net Sat Aug 14 13:53:39 2004 From: joey at joeykelly.net (Joey Kelly) Date: Sat Aug 14 13:40:14 2004 Subject: [Neworleans-pm] that link I said I would post Message-ID: <200408141353.39537.joey@joeykelly.net> Guys, I mentioned that I had a bookmark of something-or-other that I would post when I got home. I'm happy to do it, if someone would be so kind as to remind me what it was about ;-) Thanks. -- Joey Kelly < Minister of the Gospel | Linux Consultant > http://joeykelly.net "I may have invented it, but Bill made it famous." --- David Bradley, the IBM employee that invented CTRL-ALT-DEL From donnie at solomonstreet.com Sun Aug 15 23:35:40 2004 From: donnie at solomonstreet.com (Donnie Cameron) Date: Sun Aug 15 23:35:23 2004 Subject: [Neworleans-pm] Project idea and thanks In-Reply-To: References: Message-ID: <4120399C.6000303@solomonstreet.com> Hi Simon, Sorry I took so long to answer. I was so busy that I had to skip this message several times. I promise to try to reply sooner in the future. About your message: 1. It's nice to talk code, isn't it? 2. Skump is a great suggestion. What an interesting and funny concept! I definitely think this would be a good project for Perl. You could use a Java applet for capturing the drawings. Best, --Donnie Simon Dorfman wrote: > Donnie, David & Joey, > 1. Many thanks for your help and ideas about how to get started on my > project yesterday. I feel like this is actually something I can accomplish > now if I take it in small pieces. > > 2. Re: the project y'all want to work on together with cvs and such. Here's > a project idea for you: http://www.skump.org > > http://www.skump.org/how_to_play_skump.html > Is a simple explanation of the game. > > http://www.skump.org/web_based_skump.html > Is a very basic call for programmer help with some ideas of what the site > might be. > > Check it out, it's a fun game. The thing is, it may not be best suited for > perl because it involves capturing drawings. I planned to do it with flash > and php back a couple years ago when I put that site online. I don't know > if perl could do something like that (maybe in conjunction with imagemagik > or something?). Anyway, it's an idea I thought I'd throw out there. > > Simon > > > _______________________________________________ > NewOrleans-pm mailing list > NewOrleans-pm@mail.pm.org > http://mail.pm.org/mailman/listinfo/neworleans-pm > From estrabd at yahoo.com Mon Aug 16 08:36:48 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Mon Aug 16 08:36:52 2004 Subject: [Neworleans-pm] summary up, in need of review Message-ID: <1092663408.6842.202414845@webmail.messagingengine.com> Snaps to Donnie for sending me a nice summary. I posted it at http://neworleans.pm.org/index.cgi?Meeting-August-13-2004 ... please review for any additions/corrections. Thanks, Brett ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com From estrabd at mailcan.com Sat Aug 21 14:46:07 2004 From: estrabd at mailcan.com (Brett D. Estrade) Date: Sun Aug 22 08:37:15 2004 Subject: [Neworleans-pm] Fwd: Solutions and Discussion for Perl Quiz of the Week #21 (Expert Edition) Message-ID: <1093117567.32445.202787742@webmail.messagingengine.com> http://www.brettsbsd.net/~estrabd ----- Original message ----- From: "Aaron Crane" To: perl-qotw@plover.com Date: Sat, 21 Aug 2004 11:03:11 -0400 Subject: Solutions and Discussion for Perl Quiz of the Week #21 (Expert Edition) Sample solutions and discussion Perl Expert Quiz of The Week #21 (20040812) [ Aaron sent this to me on time, but I have been behind schedule in handling QOTW materials because I have had to work on my book. I will send out both regular and expert quiz #22 this Wednesday. Thanks for your patience. - MJD ] In the 1960's, the grad students at the University of Chicago math department worked on a series of astoundingly useless and time-consuming puzzles. One of these follows. Consider the set of all possible strings of the alphabet ('a' .. 'z'). Let us agree to consider two strings "equivalent" if the following conditions hold: 1. They contain precisely the same letters, and 2. They both appear in Webster's Third International Dictionary. In such a case, the two strings are considered interchangeable in all contexts. For example, "am" and "ma" are equivalent, and this also implies that "amount" and "maount" are equivalent, as are "grammar" and "grmamar" and "gramamr" and "grmaamr". Moreover, equal letters can be cancelled from the front and back end of any string. For example, "abby" and "baby" are equivalent, and, cancelling the trailing "by", this implies that "ab" and "ba" are also equivalent, and can be exchanged anywhere. When two letters can be exchanged in this way, we say that they "commute". The third floor of the math building at UC had a huge 26x26 chart; the square in column i and row j contained a proof that letters i and j would commute. Sometimes these proofs can be rather elaborate. For example, the dictionary has "dire" and "ride", so, by cancelling the trailing "e"s, one has "dir" = "rid". The dictionary also has dirten = rident (No, I don't know what those mean.) Since "dir" = "rid" we have: rident = dirent and since "rident" = "dirten", dirten = dirent even though "dirent" is not a dictionary word. Cancelling the leading "dir" leaves: ten = ent but ten = net because "ten" and "net" are dictionary words, so ent = net and, cancelling the "t", en = ne and we've just proved that "en" and "ne" commute. This fact might be useful in later proofs. What's the point of all this? Well, the goal is to find out which letters commute with *every* other letter; such letters are said to be in the "center" of the system. As for the *point*, I'm not sure there is one. Apparently the math grads at UC didn't have enough to occupy their time. The chart in the UC math building has since been lost, so your task is to write a program whose input is a word list, with one word per line, and which makes appropriate deductions and eventually computes the center of the system. I don't have the headword list from Webster's Third, but I do have the list from Webster's Second, so let's use that. You can get a copy from http://perl.plover.com/qotw/words/Web2.bz2 http://perl.plover.com/qotw/words/Web2.gz ---------------------------------------------------------------- The mailing list had an initial brief flurry of discussion about the equivalence rules. Mark offered a useful clarification: Two strings are equivalent if conditions (1) and (2) hold. Moreover, if A is equivalent to B and B is equivalent to C, then A is equivalent to C. Moreover, if A is equivalent to B, then pAq is equivalent to pBq for all strings p and q. Moreover, if pAq is equivalent to pBq for some strings p, q, A, and B, then A is equivalent to B. Nothing else is equivalent. Suppose L and M are strings of length 1; that is, single letters. We say that L and M commute if and only if LM is equivalent to ML. Two people posted solutions to the list. Rod Adams approached the problem in what he described as a 'brute-force' way: repeatedly using the inference rules to derive new equivalences. Since the dictionary is large to begin with (234936 words), and the cancellation and transitivity rules add more equivalences, his solution rapidly accumulates a very large data set. Rod used the MySQL relational database to accommodate this data set. (It occurred to me that it might have been possible to use the DBD::SQLite module instead. SQLite offers a SQL relational DBMS without needing a server, though with a few restrictions compared to server-based databases.) Unfortunately, Rod's program took a great deal of time to execute. Running it overnight on my computer produced only three of the letters in the center; I stopped it in the morning before it could get any further. The other solution posted was by Daniel Martin. Daniel's approach was in essence quite similar to Rod's. However, Daniel's program aggressively prunes the search space: as new equivalences are found, existing longer ones are thrown away if they can be derived from the current set of equivalences. This allows the center to be found in about 30 seconds on my computer. Daniel's solution also has the pleasing property that it constructs the outline of a proof for each commutative pair. Daniel's approach iterates over the set of equivalences. His reduce() function attempts to break a fact into AB=CD where either A=C or B=D is known. If both are known, the original fact is simply deleted. The fact is replaced with B=D if only A=C is known, and with A=C is only B=D is known. [ There was some fascinating discusion on the -discuss list about whether these deletions were truly safe. Eventually everyone involved agreed that they were. That part of the discussion is available at http://perl.plover.com/~alias/list.cgi?1:sss:1979 if you want to read it. - MJD ] sub reduce ($\$) { my ($fact, $found_something_flag) = @_; my ($worda, $wordb) = split('=', $fact); my ($foundpossible) = 0; my ($startat) = 1; my (@worda) = split(//,$worda); my (@wordb) = split(//,$wordb); if ($worda eq $wordb) { # we shouldn't be calling this with say-nothing facts print STDERR "Huh? $fact:\n$known{$fact}\n"; delete $interesting{$fact}; return qw[]; } while (substr($worda,0,$startat+1) eq substr($wordb,0,$startat+1)) { $startat++; } for my $i ($startat..$#worda) { @worda[0..$i-1] = sort @worda[0..$i-1]; @wordb[0..$i-1] = sort @wordb[0..$i-1]; if (join('',@worda[0..$i-1]) eq join('',@wordb[0..$i-1])) { $foundpossible = 1; my $lefta = substr($worda,0,$i); my $leftb = substr($wordb,0,$i); my $righta = substr($worda,$i); my $rightb = substr($wordb,$i); my $reason; if ($reason = knownp($lefta, $leftb)) { if (knownp($righta, $rightb)) { # This fact is merely the aggregation # of other stuff we know delete $interesting{$fact}; return qw[]; } else { $$found_something_flag = 1; delete $interesting{$fact}; my $newfact = join('=', $righta,$rightb); $reason = join("\n", $known{$fact}, ">$fact", $reason); $reason =~ s/^/ /mg; $newfact =~ s/^(\w+)(\w+)=\1/$2=/; $newfact =~ s/(\w+)=(\w+)\1$/=$2/; $newfact = join('=', sort split(/=/,$newfact)); if (! $known{$newfact} ) { $known{$newfact} = $reason; if (length($newfact)>5) { $interesting{$newfact}=1; } return ($newfact); } else { return qw[]; } } } elsif ($reason = knownp($righta, $rightb)) { $$found_something_flag = 1; delete $interesting{$fact}; my $newfact = join('=', $lefta, $leftb); $reason = join("\n", $known{$fact}, ">$fact", $reason); $reason =~ s/^/ /mg; $newfact =~ s/^(\w+)(\w+)=\1/$2=/; $newfact =~ s/(\w+)=(\w+)\1$/=$2/; $newfact = join('=', sort split(/=/,$newfact)); if (! $known{$newfact} ) { $known{$newfact} = $reason; if (length($newfact)>5) { $interesting{$newfact}=1; } return ($newfact); } else { return qw[]; } } } } if ($foundpossible) { return ($fact); } else { return qw[]; } } The knownp() function referred to in reduce() is a semipredicate returning the reason its arguments are known to be equivalent, or false if they aren't known to be equivalent. reduce() is called repeatedly on the set of equivalences. If it fails to reduce any to simpler forms, the next phase involves using existing commutative pairs to derive new equivalences, taking advantage of the interchangeability of the commutative pairs. This function uses string eval to create a function which does the interchanging: sub make_mangle_sub { my @twoletterequivs = grep(5==length, keys %known); my @twoletterswaps = sort map { split('='); } @twoletterequivs; my %lettercommutes=(); for my $letter ('a'..'z') { my @swaps = grep /^$letter/, @twoletterswaps; if (@swaps) { $lettercommutes{$letter} = join('', sort map { substr($_,1) } @swaps); } } my @letters_to_swap = sort {length($lettercommutes{$a}) <=> length($lettercommutes{$b}) or $a cmp $b} keys %lettercommutes; my $mangle_sub_text = "sub { my \$fact = shift;\n"; for my $letter (@letters_to_swap) { my $otherlet = $lettercommutes{$letter}; $mangle_sub_text .= "\$fact =~ s/([$otherlet]+)($letter+)/\$2\$1/g;\n"; } $mangle_sub_text .= "\$fact = join('=', sort split(/=/,\$fact));\n"; $mangle_sub_text .= '$fact; }'; eval ($mangle_sub_text); } If applying the 'mangling' function to each equivalence fails to produce any new facts, then all derivable equivalences have been found. At this point, any letters which commute with all other letters are in the center. Daniel's solution identifies the center as: a c e i l m n o p r s t u Thanks to all who participated, including those who took part and didn't post a solution to the list. From EmailLists at SimonDorfman.com Tue Aug 24 14:25:10 2004 From: EmailLists at SimonDorfman.com (Simon Dorfman) Date: Tue Aug 24 14:25:14 2004 Subject: [Neworleans-pm] WebGUI- perl content management system Message-ID: Has anyone on the list heard of or tried WebGUI? Simon http://www.plainblack.com/webgui What is WebGUI? WebGUI is an application framework that handles content management. Once you start using WebGUI it will become clear how well it handles content management, but it may not be clear how easy it is to extend to do other tasks. If you're used to using application servers then you're probably used to the app server handling all the mundane tasks like database connectivity, session management, and security. WebGUI does all those things, but it goes one step further. When you build a wobject in WebGUI, you're actually building a reusable application object. Now you're probably thinking, what does that mean? What it means is that once your application is built, it can be reused over and over on different portions of your site (perhaps for applications you never even thought of when you built it). For instance, once we built the message board application now we can have message boards on many different parts of the site. On most systems, you'd have to redirect the user to the "message board section" of your site. In summary, WebGUI is both a content management system and an application framework. It is used every day by many companies who wish to spend more time running their business than managing their web site, intranet, or extranet. From estrabd at yahoo.com Thu Aug 26 08:19:59 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Thu Aug 26 08:20:03 2004 Subject: [Neworleans-pm] Fwd: Perl Quiz-of-the-Week #22 Message-ID: <1093526399.24659.203108447@webmail.messagingengine.com> ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com ----- Original message ----- From: "Jose Alves de Castro" To: perl-qotw@plover.com Date: Wed, 25 Aug 2004 23:11:58 -0400 Subject: Perl Quiz-of-the-Week #22 IMPORTANT: Please do not post solutions, hints, or other spoilers until at least 60 hours after the date of this message. Thanks. Qing3 Zhu4Yi4: Qing3 Ning2 Deng3Dao4 Jie1Dao4 Ben3 Xin4Xi2 Zhi1Hou4 60 Xiao3Shi2, Zai4 Fa1Biao3 Jie3Da2, Ti2Shi4, Huo4 Qi2Ta1 Hui4 Xie4Lou4 Da2An4 De5 Jian4Yi4. Xie4Xie4. ---------------------------------------------------------------- The purpose of this problem is very simple (and hopefully something many of us will be able to use). Inside a directory (say $ENV{HOME}/.upcoming) we have several files. Here's part of one: 02/26 l?on brocard 03/06 michelangelo 05/29 simon cozens 12/28 randal schwartz 02/27 eduardo nuno 03/05 crapulenza tetrazzini 03/16 richard m. stallman This particular file is appropriately named 'birthdays'. You can have as many different files as you wish in that directory. Here's part of another file, 'events': 01 payday 15 payday 08/13/2004 slides for YAPC::EU::2004 03/01 feast of st. david 03/01/1565 Rio de Janeiro founded 03/09/2004 dentist appointment 10:00 As you can see, both the month and the year are optional. When not given a month, we'll have three spaces; by the end of the date we may have as many spaces and/or tabs up to the description. The 'events' file says that payday occurs on the 1st and 15th of every month, and that the Feast of St. David occurs each year on the first day of March. This week's problem consists of writing the script 'upcoming', which tells us about our upcoming events. Suppose today is 26 February. Then the output will contain: birthdays ===> 02/26 l?on brocard --> 02/27 eduardo nuno events --> 03/01 payday --> 03/01 Feast of St. David Explanation: * For each file, you get a paragraph, if there are upcoming events mentioned in that file. * The program will print all the events that will occur in the next 'n' days, where 'n' is specified with a '-n' command-line flag. If '-n' is omitted, 'n' will default to 7 days. * For each event, you get a string that tells you about the event's proximity: 0 => ' ===>', 1 => ' -->', 2 => ' -->', 3 => ' -->', 4 => '-->', 5 => '->', 6 => '>', 7 => ' ', If the '-n' switch is given, and the event is in the specified range, but more then 7 days ahead, then the proximity string is something like (8) or (13) depending on how many days ahead the event is. Here we're running the program on 26 February, as before, but with the option '-n 12': birthdays ===> 02/26 l?on brocard --> 02/27 eduardo nuno (8) 03/05 crapulenza tetrazzini (9) 03/06 Michelangelo events --> 03/01 Feast of St. David --> 03/01 payday (12) 03/09 dentist appointment 10:00 Note that the founding of Rio de Janeiro did not occur in either output, since it has already passed. As you'll notice, it's a little hard to schedule things such as the fourth Thursday of each month, or dates like Mother's Day (I don't know about the rest of the world, but that changes, here in Portugal). It might be a good idea to find a reasonable way to solve this. Happy hacking :-) From estrabd at yahoo.com Thu Aug 26 08:20:17 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Thu Aug 26 08:20:20 2004 Subject: [Neworleans-pm] Fwd: Perl 'Expert' Quiz-of-the-Week #22 Message-ID: <1093526417.24745.203108462@webmail.messagingengine.com> ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com ----- Original message ----- From: "Mark Jason Dominus" To: perl-qotw@plover.com Date: Wed, 25 Aug 2004 23:26:57 -0400 Subject: Perl 'Expert' Quiz-of-the-Week #22 IMPORTANT: Please do not post solutions, hints, or other spoilers until at least 60 hours after the date of this message. Thanks. IMPORTANT: S'il vous pla?t, attendez au minimum 60 heures apr?s la date de ce message avant de poster solutions, indices ou autres r?v?lations. Merci. BELANGRIJK: Stuur aub geen oplossingen, hints of andere tips in de eerste 60 uur na het verzendingstijdstip van dit bericht. Waarvoor dank. Qing3 Zhu4Yi4: Qing3 Ning2 Deng3Dao4 Jie1Dao4 Ben3 Xin4Xi2 Zhi1Hou4 60 Xiao3Shi2, Zai4 Fa1Biao3 Jie3Da2, Ti2Shi4, Huo4 Qi2Ta1 Hui4 Xie4Lou4 Da2An4 De5 Jian4Yi4. Xie4Xie4. ---------------------------------------------------------------- Write a program, 'wordladder', which gets two arguments, which are words of the same length, and which constructs and prints a "word ladder" from the first word to the second word. A word ladder from word AAA to word BBB is a sequence of dictionary words such that: 1. the first word in the sequence is word AAA 2. each word in the sequence after the first differs from the previous word in exactly one letter position 3. the last word in the sequence is word BBB For example, given the two words "love" and "hate", the program might print the word ladder: love hove have hate Or it might print: love lave have hate It might also print a longer word ladder, such as love lore lobe robe role rose lose lost most mosh moth math hath hate If the program is unable to find a word ladder, it should print an appropriate error message to the standard error, and exit with a failure status. The program should also accept an optional third argument, which, if specified, is the name of a dictionary file which contains the permissible words. If the third argument is omitted, the program should use a default dictionary. Sample word lists are available from http://perl.plover.com/qotw/words/ From jwthompson2 at gmail.com Sun Aug 29 23:15:02 2004 From: jwthompson2 at gmail.com (James W. Thompson, II) Date: Sun Aug 29 23:15:08 2004 Subject: [Neworleans-pm] [OT] 6 Available Gmail Accounts Up for Grabs! Message-ID: <4271b74204082921156aedcc65@mail.gmail.com> I have 6 Gmail invitations to give out and am offering on a first come, first served basis. Grab them while you can. http://gmail.google.com/gmail/a-5cb03dc1c9-e7e09b56fb-479b98b8bc http://gmail.google.com/gmail/a-5cb03dc1c9-c833787037-045d087df3 http://gmail.google.com/gmail/a-5cb03dc1c9-2465443bb0-3a1f06a6e3 http://gmail.google.com/gmail/a-5cb03dc1c9-548e8ffe38-563eb41fbd http://gmail.google.com/gmail/a-5cb03dc1c9-3ce74757d8-501f88b599 http://gmail.google.com/gmail/a-5cb03dc1c9-721a8fae6f-cfaf13a083 Happy Pickings! -- James W. Thompson, II (New Orleans, LA) From estrabd at yahoo.com Tue Aug 31 14:02:29 2004 From: estrabd at yahoo.com (E. Strade, B.D.) Date: Tue Aug 31 14:02:32 2004 Subject: [Neworleans-pm] Fwd: Solutions and Discussion for Perl Quiz of the Week #22 Message-ID: <1093978949.31318.203429575@webmail.messagingengine.com> ===== http://www.brettsbsd.net/~estrabd __________________________________ Do you Yahoo!? Yahoo! SiteBuilder - Free, easy-to-use web site design software http://sitebuilder.yahoo.com ----- Original message ----- From: "Jose Alves de Castro" To: perl-qotw@plover.com Date: Tue, 31 Aug 2004 14:56:18 -0400 Subject: Solutions and Discussion for Perl Quiz of the Week #22 Sample solutions and discussion Perl Quiz of The Week #22 (20040825) The purpose of this problem is very simple (and hopefully something many of us will be able to use). Inside a directory (say $ENV{HOME}/.upcoming) we have several files. Here's part of one: 02/26 l\xE9on brocard 03/06 michelangelo 05/29 simon cozens 12/28 randal schwartz 02/27 eduardo nuno 03/05 crapulenza tetrazzini 03/16 richard m. stallman This particular file is appropriately named 'birthdays'. You can have as many different files as you wish in that directory. Here's part of another file, 'events': 01 payday 15 payday 08/13/2004 slides for YAPC::EU::2004 03/01 feast of st. david 03/01/1565 Rio de Janeiro founded 03/09/2004 dentist appointment 10:00 As you can see, both the month and the year are optional. When not given a month, we'll have three spaces; by the end of the date we may have as many spaces and/or tabs up to the description. The 'events' file says that payday occurs on the 1st and 15th of every month, and that the Feast of St. David occurs each year on the first day of March. This week's problem consists of writing the script 'upcoming', which tells us about our upcoming events. Suppose today is 26 February. Then the output will contain: birthdays ===> 02/26 l\xE9on brocard --> 02/27 eduardo nuno events --> 03/01 payday --> 03/01 Feast of St. David Explanation: * For each file, you get a paragraph, if there are upcoming events mentioned in that file. * The program will print all the events that will occur in the next 'n' days, where 'n' is specified with a '-n' command-line flag. If '-n' is omitted, 'n' will default to 7 days. * For each event, you get a string that tells you about the event's proximity: 0 => ' ===>', 1 => ' -->', 2 => ' -->', 3 => ' -->', 4 => '-->', 5 => '->', 6 => '>', 7 => ' ', If the '-n' switch is given, and the event is in the specified range, but more then 7 days ahead, then the proximity string is something like (8) or (13) depending on how many days ahead the event is. Here we're running the program on 26 February, as before, but with the option '-n 12': birthdays ===> 02/26 l\xE9on brocard --> 02/27 eduardo nuno (8) 03/05 crapulenza tetrazzini (9) 03/06 Michelangelo events --> 03/01 Feast of St. David --> 03/01 payday (12) 03/09 dentist appointment 10:00 Note that the founding of Rio de Janeiro did not occur in either output, since it has already passed. As you'll notice, it's a little hard to schedule things such as the fourth Thursday of each month, or dates like Mother's Day (I don't know about the rest of the world, but that changes, here in Portugal). It might be a good idea to find a reasonable way to solve this. Happy hacking : ---------------------------------------------------------------- This week's problem had four submissions, by Roger West, Zed Lopez Dave Cash and Mark Dominus. Thanks, guys :-) [ Code for the four solutions can be found at http://perl.plover.com/qotw/misc/r022/ - MJD ] Their four solutions have different ways of solving the problem, which we'll discuss below. Though the problem wasn't all that hard, something terrible happened... it was solved in mid August, and tested by the end of August... and you'll see in a moment what happened because of that :-) First, let's talk about input. The problem stated: > both the month and the year are optional Here are the four types of dates this would allow for: 09/01/2004 should pass 1 09/01 should pass 2 01/2004 should pass 3 01 should pass 4 As you'll notice, the first date has month, day and year, the second does not contain the year (optional), the third does not contain the day (optional) and the last one has only the day (thus not including both optional parameters). For a test suite regarding dates, here's what I used: 09/01/2004 should pass 1 09/01 should pass 2 01/2004 should pass 3 01 should pass 4 09/01/2003 should NOT pass a 09/01/2005 should NOT pass b 08/01 should NOT pass c 09/11 should NOT pass d 01/2003 should NOT pass e 01/2005 should NOT pass f 29 should NOT pass g 18 should NOT pass h You'll notice that all this input is valid. Given that I ran the tests on August 30th, all of the first four tests should be displayed. As for the others, none of them should. Tests a,c,e,g contain dates that have already passed, while tests b,d,f,h all have dates that are not in the 7 days range from today (Aug 30th). Here are the results for these tests: 1 2 3 4 a b c d e f g h roger _ _ x _ _ _ _ _ _ _ _ _ zed _ _ E x _ _ _ _ E E _ _ dave _ _ _ _ _ _ _ _ _ _ x _ mark _ _ _ _ _ _ _ _ _ _ _ _ _ - the entry "was displayed" for test 1-4 or "wasn't displayed" for a-h x - the entry "wasn't displayed" for test 1-4 or "was displayed" for a-h E - fatal error As you can see, most of the solutions had one problem or another (hey, dates are tricky...) [ I also wonder what these programs would have done on December 30 for dates that occur the following January. I was at some pains to get this right, but I think it's a subtle point. For example, when your program sees "09/11" it's tempting to have it assume that the year defaults to *this* year, but in the case of "01/11" it should default to *next* year, unless the current date is in early January. - MJD ] Let's see what went wrong: Roger's Solution: Test 3: Roger's code has five different regexps to get all the possible cases... here's what they do: first - catches dates having only the day second - catches complete dates third - catches dates without the year fourth - ?? fifth - ?? I gave up at trying to understand what the last two ones did, but I would bet it has something to do with the "2nd tuesday of every month" format... (am I wrong?) There doesn't seem to be any regexp to catch dates without the month, but with the year... Zed's Solution: Test 3: Module Date::Calc produced an error with this test. To comprehend the problem, take a look at this regexp: m|^\s*(\d+)(?:/(\d+))?(?:/(\d*))?\s+(.*)$| Here's the problem: since no month was provided, $1 (given to $month) captures the day instead of the month. That would be OK, as later on, if $day has no value, $day gets the value of $month and $month another value... the problem is that this regexp puts the day in the month and the year in the day. Since it has no year, $year gets the value of the current year, and hence we have day, month and year, but what we really have for day is the month, and for the month, the year; we have this date: 2004/01/2004. Date::Calc, as it should, complains about this. Test 4: It fails because the month, if not available, is replaced with the current month. Given that I did the test on one of the last days of August, the date was considered as being 08/01/2004, which had indeed already passed. 09/01/2004 wasn't considered, but should have been. Test e: Same thing as test 3. Test f: Same thing as test 3. Dave's Solution: Test g: For some strange reason, Dave's solution regards yesterday's events as today's ones :-| It still does consider today's events as it should, though. Since Dave said nothing about this (at least that I remember of), I don't consider this a feature :-) I had nor time nor skills to discover the reason for this, but I sure would like to know... :-) Now that we've dealt with the input, let's take a quick look at the output. I tested with this: 08/30/2004 today 08/31/2004 in 1 day 09/01/2004 in 2 days 09/02/2004 in 3 days 09/03/2004 in 4 days 09/04/2004 in 5 days 09/05/2004 in 6 days 09/06/2004 in 7 days 09/07/2004 in 8 days 09/08/2004 in 9 days 09/09/2004 in 10 days Here's what I got: Roger: birthdays ===> today --> in 1 day --> in 2 days --> in 3 days --> in 4 days -> in 5 days > in 6 days in 7 days Zed: birthdays ===> 08/30 today --> 08/31 in 1 day --> 09/01 in 2 days --> 09/02 in 3 days --> 09/03 in 4 days -> 09/04 in 5 days > 09/05 in 6 days 09/06 in 7 days Dave: birthdays: ===> 08/30 today ===> 08/31 in 1 day --> 09/01 in 2 days --> 09/02 in 3 days --> 09/03 in 4 days --> 09/04 in 5 days -> 09/05 in 6 days > 09/06 in 7 days 09/07 in 8 days Mark: birthdays: ===> 8/30/2004 today --> 8/31/2004 in 1 day --> 9/ 1/2004 in 2 days --> 9/ 2/2004 in 3 days --> 9/ 3/2004 in 4 days -> 9/ 4/2004 in 5 days > 9/ 5/2004 in 6 days 9/ 6/2004 in 7 days Well... close enough :-) Roger apparently isn't printing dates, while Zed isn't indenting. Mark decided that a month is a month, and it needs no stinking zeroes :-) Did you notice something else funny? Yep, so did I... Dave's solution is not only allowing for the date of yesterday, but also for one day more then the specified range... I think those "proximity strings" are not right, either... Now that we've taken care of that, here are some other things: * Recursion: it wasn't asked for, but Zed's solution implements this quite well, via File::Find. * -n switch: Every solution dealt with this. No problem. * "2nd something of every something" format: Roger tried this. I made a test with this: 1th thursday of every month: something happens It worked, but gave me a warning... I wrote this and discovered that I was being stupid :-) Then, I used this: 1st thursday of every month: something happens It worked. No warnings :-) * Speed: I had no time to test for speed... Roger started his entry by stating that Date::Manip is slow, while Dave stated his code would run pretty fast... that's all I have to say for now... * Others: Dave implemented some other features: letting the user select the files he wants to be processed, providing an option for specifying the date format (which I didn't test much) and another for the directory to process. Bottom line: While Roger demonstrated his ability with the "Swiss Army Chainsaw of date modules", Date::Manip, making available the "2nd Tuesday of every month" format, Zed made use of File::Find for immediate recursion and Dave went with POSIX and implemented a lot of nice features. Mark, on his side, decided to go with Time::Local and actually made it work for all tested cases... O:-) I believe an even better script could be made out of these three ones, as they all have strong points. Final considerations: By using File::Find, Zed's solution made it hard for me to test it... It was trying to parse my test file, but also the swap file created by vim, given that I was editing the file :-) It took me a while to figure that out (the time for printing some debugging information including the name of the file being opened, at least). Testing your code was very fun, and something I hope to do again in a near future :-) Thanks for your time, guys :-) jac [ My thanks also to anyone who worked on the quiz and didn't send in a solution. I have a bit of a problem now. I sent out an 'expert' quiz, the word ladder one, thinking that I would write up the report about it myself, since I am on leave this week. I had not expected it to turn out to be one of the most popular quizzes ever, and now I don't think I have time to write it up. I would be grateful if someone else would volunteer to do it. Is anyone interested in looking over and testing the many submitted solutions for this quiz? If so, drop me a note at mjd@plover.com. I will be happy to offer asistance and guidance. Thanks, - MJD ]