This is part of The Pile, a partial archive of some open source mailing lists and newsgroups.
To: sfpug@sf.pm.org From: "Chris Palmer" <cecibean@bitstream.net> Subject: [sf-perl] Coderefs as hash values, interpreted at runtime? Date: Wed, 21 Nov 2001 00:35:13 GMT Hello, I've got a bit of a problem. I have a small program (shortened here for the sake of making the point) that could be nifty instead of kludgey, if only I could get the syntax right: === #!/usr/bin/perl -w use strict; my $yes; my %months = ('Jan' => 31, 'Feb' => &leap_year(), 'Mar' => 31, 'Apr' => 30 # other months here ); $yes = 0; print $months{Feb}; $yes = 1; print $months{Feb}; sub leap_year { if ($yes) { return 29; } else { return 28; } } === You get "2828" as the output instead of the desired "2829". What I want is for any getting of the value $months{Feb} to re-invoke leap_year(), taking the value of the global boolean $yes into account each time. I had the idea of using eval {}, but several attempts to make it go yielded results similar to the above. I am also able through several permutations of the above (with and without eval) to get the dreaded SCALAR(0x1234) or CODE(0x1234) output. If you define $yes to be 1 before %months is defined, you get "2929" as output. I think what is happening is that &leap_year is compiled and the resulting code assigned to $months{Feb} right away, instead of it being reevaluated when invoked, at runtime. Any clues? Thanks. :) === To: <sfpug@sf.pm.org> From: David Lowe <dlowe@saturn5.com> Subject: Re: [sf-perl] Coderefs as hash values, interpreted at runtime? Date: Tue, 20 Nov 2001 16:58:04 -0800 (PST) Chris et. al. - The short answer: &leap_year() executes a subroutine call - what you want is to take a reference to the sub like this: \&leap_year. But this alone won't solve your problem. If I may describe your desired data structure in words: a hash, in which the keys are abbreviated month names, and the values are sometimes constant scalars and sometimes subroutine calls. The problem with this is that you're going to need conditional code everywhere you use it, too, because the data types of the values are different (sometimes scalars, sometimes subroutine references). To accomplish the goal (a hash which can be used without special handling code, but does what you want) you'll need to pick: either all scalars or all subroutine references. The latter looks like this: my %months = ( 'Jan' => sub { 31 }, 'Feb' => \&leap_year, 'Mar' => sub { 31 }, 'Apr' => sub { 30 }, ); $yes = 0; print $months{'Feb'}->(), "\n"; $yes = 1; print $months{'Feb'}->(), "\n"; sub leap_year { return $yes ? 29 : 28; } The former is more complex to implement (use tie()) but cleans up the usage a tad (you can "print $months{'Feb'}" instead of "print $months{'Feb'}->()"). === To: sfpug@sf.pm.org From: Tad McClellan <tadmc@augustmail.com> Subject: Re: [sf-perl] Coderefs as hash values, interpreted at runtime? Date: Tue, 20 Nov 2001 19:24:15 -0500 On Wed, Nov 21, 2001 at 12:35:13AM +0000, Chris Palmer wrote: > my $yes; > my %months = ('Jan' => 31, > 'Feb' => &leap_year(), ^^ ^^ parens always mean "call the sub now" 'Feb' => \&leap_year, # take a ref rather than call the sub > 'Mar' => 31, > 'Apr' => 30 > # other months here > ); > > $yes = 0; > print $months{Feb}; print $months{Feb}->(); # deref and call it > $yes = 1; > print $months{Feb}; print $months{Feb}->(); # deref and call it > sub leap_year { > if ($yes) { > return 29; > } else { > return 28; > } > } You might want to make _all_ of the values in %months be code refs, then you won't have to special case February: my %months = ('Jan' => sub {31}, 'Feb' => \&leap_year, 'Mar' => sub {31}, 'Apr' => sub {30} # other months here ); You might also want to pass $yes as an argument. Now that they are all code refs, you don't need for it to be global. print $months{Feb}->($yes); sub leap_year { if ($_[0]) { ... === To: sfpug@sf.pm.org From: Paul Makepeace <Paul.Makepeace@realprogrammers.com> Subject: Re: [sf-perl] Coderefs as hash values, interpreted at runtime? Date: Tue, 20 Nov 2001 17:06:48 -0800 On Wed, Nov 21, 2001 at 12:35:13AM +0000, Chris Palmer wrote: > Hello, > > I've got a bit of a problem. I have a small program (shortened here for the > sake of making the point) that could be nifty instead of kludgey, if only I > could get the syntax right: > > === > #!/usr/bin/perl -w > > use strict; > > my $yes; > my %months = ('Jan' => 31, > 'Feb' => &leap_year(), > 'Mar' => 31, > 'Apr' => 30 > # other months here > ); > > $yes = 0; > print $months{Feb}; > $yes = 1; > print $months{Feb}; There's no way directly to do what you want without doing one of two things, checking for the existence of a CODE ref or tie'ing the hash and doing the CODE ref test inside the tie'd package. # note \ to get CODE ref Feb => \&leap_year, ... sub leap_year { my $y = 1996; #shift || (localtime)[5]; 28 + (($y % 4) == 0 && ($y % 100) != 0) } my $month = 'Feb'; my $m = $months{$month}; my $days = ref $m ? &$m : $m; print "$month has $days days\n"; === To: sfpug@sf.pm.org From: David Wheeler <david@Wheeler.net> Subject: Re: [sf-perl] Coderefs as hash values, interpreted at runtime? Date: 20 Nov 2001 17:10:25 -0800 AFAICT, there's no simple way to do this without using coderefs. Here's my solution: #!/usr/bin/perl -w use strict; my $yes; my %months = ('Jan' => sub { 31 }, 'Feb' => \&leap_year, 'Mar' => sub { 31 }, 'Apr' => sub { 30 } # other months here ); $yes = 0; print &{$months{Feb}}; $yes = 1; print &{$months{Feb}}; sub leap_year { $yes ? 29 : 28 } However, you might be better off using a CPAN module. ===