http://qs1969.pair.com?node_id=144232

sifukurt has asked for the wisdom of the Perl Monks concerning the following question:

I've got a module that I'm working on where I am pre-caching a number of very large constants, created with Math::BigFloat. My problem is this: I've got 10 or 12 of these very large constants, but for the most part, only one or two of them are going to used at any given time. That being the case, I don't want to automatically dump all of them into memory, only the ones that a user asks for. For example, I've got the square root of 2 and the square root of 5 ($_sqrt2_ and $_sqrt5_, respectively) as two of these constants. Let's say I only need $_sqrt2_. What would be the best method for creating these via Math::BigFloat "on demand?" My first thought was to make a CONSTANT() routine where the user would pass the name of the constant they want and have it created at that time. It seems to me, though, that there should be a better way. I did some digging through the search here, but didn't find anything (perhaps I was searching for the wrong thing?). Any suggestions?

Many thanks.
___________________
Kurt

UPDATE (11-Feb-2002 10:21:38 AM): By way of further clarification, the problem isn't that I'm concerned about the calculation time of these constants. I'm pre-calculating them and I'm subsequently hard-coding them as Math::BigFloat objects. My concern is a memory issue since some of these constants are very, very large. I suppose a few extra kb of memory probably isn't going to make much of a difference, but it is The Principle of The Thing. My original plan was to use an accessor method similar to that described by hossman to avoid dumping all of these constants into memory if only one or two (or possibly even none) are going to be used in a given script. Forgive me for showing my ignorance here, but does the Tie::Scalar method that japhy used accomplish this? I've never used Tie::Scalar before, so I'm a little in the dark, even after reading the docs.

Replies are listed 'Best First'.
Re: Pre-caching large constants
by japhy (Canon) on Feb 08, 2002 at 22:12 UTC
    Here's Tie::Scalar::Once. It's not very complex.
    # delays determination of a scalar's value until needed package Tie::Scalar::Once; require Exporter; @ISA = qw( Exporter ); @EXPORT = qw( delay ); use strict; sub delay { tie $_[0], __PACKAGE__, \$_[0], $_[1]; } sub TIESCALAR { my ($class, $var, $code) = @_; bless [ $var, $code ], $class; } sub FETCH { my ($self) = @_; my $val = $self->[1]->(); untie ${ $self->[0] }; ${ $self->[0] } = $val; } sub STORE { require Carp; Carp::croak("Can't store $_[1] to a constant"); } 1;
    Use it like so:
    use Tie::Scalar::Once; delay $x => sub { sqrt 2 }; # ... if ($you_need_to) { print $x }

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker.
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Acctually, it looks like a more elaborate version of this allready exists as Data::Lazy.
Re: Pre-caching large constants
by chromatic (Archbishop) on Feb 08, 2002 at 21:26 UTC
    Write a new class derived from Tie::Scalar. In its FETCH() method, create the constant with Math::BigFloat, then replace the tied scalar with the constant. The float will only be created on the first access.

    Disclaimer: I have not (yet) tried this, but I'm pretty confident.

    Update: I have tried this, and I'm reinventing Oroborous. It's not as easy as it first sounded.

(bbfu) (Memoize) Re: Pre-caching large constants
by bbfu (Curate) on Feb 09, 2002 at 02:02 UTC

    Or, you could go with a pre-built solution, a la Memoize.

    bbfu
    Seasons don't fear The Reaper.
    Nor do the wind, the sun, and the rain.
    We can be like they are.

Re: Pre-caching large constants
by runrig (Abbot) on Feb 08, 2002 at 22:53 UTC
    Just a not too well thought out idea, but you could put the values after a _DATA_ tag so that they're there to read in the first time, e.g:
    __DATA__ SQRT5|2.blahblahblah SQRT2|1.blahblahblah E|2.more blahblah
    The logic to control the reading in is left up to you, anyway, others seem to have a handle on part of that, while I don't have the time at the moment. I don't really like the AutoLoader solution, it seems nifty, but then you get too many reports from people who install the module incorrectly (just copying the pm file to a directory in @INC, or using 'use lib', when you really need to do the 'perl Makefile.PL, make...etc.), and then ask "Why doesn't this work?".

    And then again, a few extra 500 byte strings really shouldn't make all that big of a difference...

Re: Pre-caching large constants
by lestrrat (Deacon) on Feb 08, 2002 at 21:22 UTC

    Check out AutoLoader or SelfLoader. They are nifty!

    apparently some people are not satisfied by the admission of mistake in a follow up post in the thread...

      I don't think SelfLoader or AutoLoader are really what he wants here. They are designed for situations in which the compilation of code is very expensive, and you don't want to bother unless the code is needed.

      In this case, it sounds like the code to populate the constants is relatively simple, and won't take long to compile, it just takes a while to run.

      This screams accessor to me.

      Instead of making these constants part of your 'public' API, make them internal, and write an accessor for them that initializes them the first time it calls, and returns them everytime after that:

      package mypackage; my $sqrt_2 = undef; # not visible outside of mypackage sub get_sqrt_2 { unless (defined $sqrt_2) { # do a bunch of work to set $sqrt_2 } return $sprt_2; }

        Oh I get it. Right, it's the computation in itself that's the problem. Yep, then probably not SelfLoader or AutoLoader